c++ - 在模板构建器模式中排除重复的构造函数调用

标签 c++ c++11 templates builder

考虑以下 builder -like 类,它最终允许我构造一个对象,该对象具有成员变量的某些(运行时)值,以及嵌入由几种(编译时)类型携带的某些行为。

相同的构建允许更新成员变量(通常的构建器模式),以及更改与构建器的类型携带状态相关联的模板 type 参数(仅显示了几个模板类型参数和成员,但实际上会更多):

template <typename T1 = DefaultT1, typename T2 = DefaultT2>
class Builder {
  int param1, param2;
  Builder(int param1, int param2) : param1{param1}, param2{param2} {}
public:
  Builder() : Builder(default1, default2) {}

  // methods to change param1 and param2 not shown

  /* return a new Builder with T1 changed to the given T1_NEW */
  template <typename T1_NEW>
  Builder<T1_NEW, T2   > withT1() { return {param1, param2}; }

  template <typename T2_NEW>
  Builder<T1   , T2_NEW> withT2() { return {param1, param2}; }

  Foo make() {
    // uses T1 and T2 to populate members of foo
    return Foo{ typename T1::member, typename T2::another };
  }
};

注意 withT1<>withT2<>允许您为 T1 返回具有不同类型的新构建器的方法或 T2分别。这些方法的主体是相同的:return {param1, param2}; ,实际上比这里显示的要复杂得多(例如,如果有很多参数)。

我想将主体分解成某种构造方法,例如:

template <typename T1_, typename T2_>
Builder<T1_, T2_> copy() { return {param1, param2}; }

然后每个 withT*方法可以只调用copy。

但是,我不清楚如何避免包含 Builder 的完全限定类型在通话中:

template <typename T1_NEW>
Builder<T1_NEW, T2   > withT1() { return copy<T1_NEW, T2>(); }

这里的治疗方法比原来的毒药更糟糕,因为我需要用 <T1_NEW, T2> 来限定每个复制调用(这对于每个 withT* 方法都是不同的)。有什么方法可以引用返回类型或其他类型的扣除,我可以用它来调用 copy()在每个函数中以相同的方式?

我使用 C++11 编写,但也欢迎讨论如何在以后的标准中改进 C++11 解决方案。

最佳答案

我没有 C++11 的解决方案,但正如您自己所说,C++14 可能对其他人有帮助。

如果我理解正确,您需要一个存储任意参数的类,并以一种方便的方式将所有参数传递给构造函数。这可以使用可变模板参数和 std::tuple:

来实现
#include <tuple>

template <typename... Args>
class Builder
{
public:
    explicit Builder(Args... args)
        : arg_tuple(std::forward<Args>(args)...)
    {}

    template <typename T>
    T make()
    {
        return std::make_from_tuple<T>(arg_tuple);
    }

    template <typename T>
    Builder<Args..., T> with(T t)
    {
        return std::make_from_tuple<Builder<Args..., T>>(std::tuple_cat(arg_tuple, std::make_tuple(std::move(t))));
    }

private:
    std::tuple<Args...> arg_tuple;
};

template <typename... Args>
Builder<Args...> make_builder(Args... args)
{
    return Builder<Args...>(std::forward<Args>(args)...);
}

用法:

struct Foo
{
    Foo(int x, int y)
        : x(x), y(y)
    {}
    int x;
    int y;
};

struct Bar
{
    Bar(int x, int y, float a)
        : x(x), y(y), a(a)
    {}
    int x;
    int y;
    float a;
};

int main()
{
    auto b = make_builder().with(5).with(6);
    auto foo = b.make<Foo>();  // Returns Foo(5, 6).
    auto b2 = b.with(10.f);
    auto bar = b2.make<Bar>();  // Returns Bar(5, 6, 10.f).
}

虽然 std::make_from_tuple 是 C++17,但可以使用 C++14 的特性来实现:

namespace detail
{
    template <typename T, typename Tuple, std::size_t... I>
    constexpr T make_from_tuple_impl(Tuple&& t, std::index_sequence<I...>)
    {
        return T(std::get<I>(std::forward<Tuple>(t))...);
    }
}

template <typename T, typename Tuple>
constexpr T make_from_tuple(Tuple&& t)
{
    return detail::make_from_tuple_impl<T>(
        std::forward<Tuple>(t),
        std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>>{});
}

关于c++ - 在模板构建器模式中排除重复的构造函数调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50539774/

相关文章:

c++ - 在智能指针上调用成员函数指针

c++ - 类中类似于 std::map 或 std::vector 的构造函数

具有默认参数的 C++ 模板

c++ - 在 C++ 模板编程中遇到麻烦

c++ - 如何在C++中动态分配数组

c++ - 为什么 std::vector 调整大小失败?

c++ - 如何防止可变参数构造函数优于复制构造函数?

c++ - 调试程序窗口不会关闭

c++ - 在 C++ 中解析大文本文件

c++ - 名称和派生名称类之间的连接(作为模板参数)