Bee*_*ope 14 c++ templates builder c++11
考虑以下类似于构建器的类,它最终允许我构造一个具有成员变量的某些(运行时)值的对象,以及嵌入由多个(编译时)类型携带的某些行为.
相同的构建允许更新成员变量(通常的构建器模式),以及更改与构建器的类型携带状态相关联的模板类型参数(仅显示有几个模板类型参数和成员,但在实践中,会有更多):
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 };
}
};
Run Code Online (Sandbox Code Playgroud)
请注意withT1<>,并withT2<>允许您与不同类型的返回一个新的生成器方法T1或T2分别.这些方法的主体是相同的:return {param1, param2};在实践中比这里显示的要复杂得多(例如,如果有很多参数).
我想把身体分解成一些做结构的方法,比如:
template <typename T1_, typename T2_>
Builder<T1_, T2_> copy() { return {param1, param2}; }
Run Code Online (Sandbox Code Playgroud)
然后每个withT*方法都可以调用copy.
但是,我不清楚如何避免Builder在通话中包含完全限定类型:
template <typename T1_NEW>
Builder<T1_NEW, T2 > withT1() { return copy<T1_NEW, T2>(); }
Run Code Online (Sandbox Code Playgroud)
这里的治疗方法比原来的毒药更糟糕,因为我需要对每个复制调用进行限定<T1_NEW, T2>(这对于每种withT*方法都是不同的).有没有什么方法可以引用返回类型或其他类型的演绎,我可以用它copy()在每个函数中以相同的方式调用?
我正在用C++ 11编写,但也欢迎讨论如何在后来的标准中改进C++ 11解决方案.
您可以引入一个具有隐式转换的构建器代理,以节省一些输入:
template<typename T1, typename T2>
struct Builder;
struct BuilderProxy
{
int param1, param2;
template<typename T1, typename T2>
operator Builder<T1, T2>() const { return {param1, param2}; }
};
template <typename T1, typename T2>
struct Builder {
int param1, param2;
Builder(int param1, int param2) : param1{param1}, param2{param2} {}
BuilderProxy copy() { return {param1, param2}; }
template <typename T1_NEW>
Builder<T1_NEW, T2 > withT1() { return copy(); }
template <typename T2_NEW>
Builder<T1 , T2_NEW> withT2() { return copy(); }
};
int main() {
Builder<int, int> a(1, 2);
Builder<double, double> b = a.withT1<double>().withT2<double>();
}
Run Code Online (Sandbox Code Playgroud)
我没有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)...);
}
Run Code Online (Sandbox Code Playgroud)
用法:
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).
}
Run Code Online (Sandbox Code Playgroud)
虽然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>>>{});
}
Run Code Online (Sandbox Code Playgroud)
不确定我是否 100% 理解你的问题。无论如何,让我尝试一个初步的答案:我在您的代码片段中添加了一个模板化的隐式转换运算符,该运算符正在调用reinterpret_cast
template<typename U1, typename U2>
operator Builder<U1, U2>(){ return *reinterpret_cast<Builder<U1, U2>*>(this); }
Run Code Online (Sandbox Code Playgroud)
这通常是很麻烦且不安全的,但在你的情况下它可以完成工作。它允许 withT1 和 withT2 成员函数类似于
template <typename T1_NEW>
Builder<T1_NEW, T2 > withT1() { return *this; }
template <typename T2_NEW>
Builder<T1 , T2_NEW> withT2() { return *this; }
Run Code Online (Sandbox Code Playgroud)
我用于测试代码的片段附在下面
#include <type_traits>
template<typename T1, typename T2>
struct Foo;
template<>
struct Foo<int, double>{
int mInt;
double mDouble;
};
template<>
struct Foo<char, double>{
char mChar;
double mDouble;
};
struct t1{
using type = int;
static type member;
};
struct t2{
using type = double;
static type another;
};
struct tt1{
using type = char;
static type member;
};
template <typename T1 = t1, typename T2 = t2>
class Builder {
// int param1, param2;
Builder(int param1, int param2) : param1{param1}, param2{param2} {}
public:
int param1, param2;
Builder() : Builder(0, 0) {}
template<typename U1, typename U2>
operator Builder<U1, U2>(){ return *reinterpret_cast<Builder<U1, U2>*>(this); }
template <typename T1_NEW>
Builder<T1_NEW, T2 > withT1() { return *this; }
template <typename T2_NEW>
Builder<T1 , T2_NEW> withT2() { return *this; }
Foo<typename T1::type, typename T2::type> make() {
// uses T1 and T2 to populate members of foo
return Foo<typename T1::type, typename T2::type>{T1::member, T2::another};
}
};
int main(){
Builder<t1, t2> b;
auto c = b.withT1<tt1>();
static_assert(std::is_same<decltype(c), Builder<tt1, t2>>::value, "error");
}
Run Code Online (Sandbox Code Playgroud)