我有一个构建器类,我想将参数存储为引用,以供后续构建中使用。
我想将可变数量的参数传递给我的类,使用类模板参数推导来推断模板参数,并将那些传递的参数作为引用存储在std :: tuple中。
从参数包转换为引用的std :: tuple的最简单方法是什么?
我发现std :: forward_as_tuple的功能与我想要的类似,但是我不希望有转发引用,而且它在成员元组的初始化期间会给出语法错误。
template <typename... Ts>
struct Builder
{
using ArgsT = decltype(std::forward_as_tuple(Ts{}...));
ArgsT args_;
Builder(Ts&... ts) : args_(ts...) {}
};
int main()
{
struct Foo
{
int a;
double b;
};
Foo foo{};
Builder fooBuilder{foo.a, foo.b};
}
语法错误是:
错误:没有匹配的函数可以调用
std::tuple<int&&, double&&>::tuple(int&, double&)
答案 0 :(得分:5)
如果您只想参考,请直接使用它们:
template <typename... Ts>
struct Builder
{
using ArgsT = std::tuple<Ts&...>;
ArgsT args_;
Builder(Ts&... ts) : args_(ts...) {}
};
int main()
{
struct Foo
{
int a;
double b;
};
Foo foo{};
Builder fooBuilder{foo.a, foo.b};
}
输入您的代码
decltype(std::forward_as_tuple(Ts{}...))
在std::tuple<Ts&&...>
中解析。
Ts{}
创建一个临时文件(并要求您的类型是默认可构造的)。
您不能将int&
绑定到int&&
。
您可以使用decltype(std::forward_as_tuple(std::declval<Ts&>()...))
,它以std::tuple<Ts&...>
进行解析,但后来更简单且提供了解决方案;-)。
答案 1 :(得分:2)
另一种方式:
#include <tuple>
template<typename Tuple>
struct Builder
{
Tuple args_;
Builder(Tuple const& t) : args_(t) {}
};
int main()
{
struct Foo
{
int a;
double b;
};
Foo foo{};
Builder fooBuilder{std::tie(foo.a, foo.b)};
}
答案 2 :(得分:2)
Builder(Ts&... ts)
是一组左值引用。
Ts{}...
是一包相同类型的prvalue。
std::forward_as_tuple(Ts{}...)
是一个元组,其中包含对相同类型的右值引用。
左值引用和右值引用不是同一回事。您不能将一个分配给另一个。因此args_(ts...)
会生成适当的错误消息。
有多种方法可以解决您的问题。
template <typename... Ts>
struct Builder
{
using ArgsT = std::tuple<Ts&&...>; // (1)
using ArgsT = std::tuple<Ts&...>; // (2)
using ArgsT = std::tuple<Ts...>; // (3)
所有这三种方法实际上都是解决问题的合理方法,具体取决于以后的代码选项。根据您的实际问题选择一个。
ArgsT args_;
这里是什么:
Builder(Ts&&... ts) : args_(std::forward<Ts>(ts)...) {}
Builder(Ts&... ts) : args_(ts...) {}
Builder(Ts&&... ts) : args_(std::forward<Ts>(ts)...) {}
以上3种情况中的每一种。
在情况(1)中,您可以将参数完美地转发到元组中,并且元组存储对任何右值参数的右值引用。在情况(2)中,您仅采用左值参数,并存储对它们的左值引用的元组。在情况(3)中,您可以将参数完美地转发到元组中,并在参数为右值而左值引用为左值时存储值。
(3)在希望引用超出当前行时很有用;唯一安全的方法是存储副本,然后将其移入。
如果只想引用左值,(2)很有用。
如果您的Builder对象不会超过当前行,并且您甚至不想为移动对象付费,则(1)很有用。比(1)还危险。
全部3个都将使您的示例代码编译。