c++ - Factoring out repeated constructor calls in template-builder pattern
问题描述
Consider the following builder-like class, which ultimately allows me to construct an object with both certain (runtime) values for member variables, as well as embedding some behavior which is carried by several (compile-time) types.
The same build allows one to update member variables (the usual builder pattern), as well as change template type parameters associated with the type-carried state of the builder (only shown with a couple of template type parameters and members, but in practice, there would be more):
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 };
}
};
Note the withT1<>
and withT2<>
methods which allow you to return a new builder with a different type for T1
or T2
respectively. The bodies for these methods are identical: return {param1, param2};
, and in practice much more complicated than shown here (e.g., if there are many parameters).
I'd like to factor the body out into some method which does the construction, like:
template <typename T1_, typename T2_>
Builder<T1_, T2_> copy() { return {param1, param2}; }
and then each withT*
method could just call copy.
However, it isn't clear to me how to avoid including the fully qualified type of Builder
in the call:
template <typename T1_NEW>
Builder<T1_NEW, T2 > withT1() { return copy<T1_NEW, T2>(); }
Here the cure is worse than the original poison since I need to qualify each copy call with <T1_NEW, T2>
(and this is different for each withT*
method). Is there some way I can refer to the return type or another type of deduction which I can use to call copy()
in the same way in each function?
I'm writing in C++11, but discussion of how a C++11 solution could be improved in later standards is also welcome.
解决方案
我没有 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>>>{});
}
推荐阅读
- wordpress - 如何插入媒体库中的图像并将其设置为前端帖子表单中的特色图像
- python - 拉出分类行并应用于所有后续行,直到找到新类别
- r - 为什么航空旅客数据集的暗淡在 R 中为空?
- powershell - Set-ADuser 和“用户下次登录时必须更改密码”的问题
- c++ - 密码学。获得最差的加密结果
- android - 如何在灯光模式下使用flutter_tex(Flutter)?
- node.js - 如果网站未运行,nodemailer 不会发送电子邮件
- python - 我们如何在桑基图中格式化数字并在图表之外设置标签?
- javascript - mongoose findbyId() 显示“路径”参数必须是字符串类型。收到模型实例'
- perl - 迭代多级哈希