如何从参数包构造 std::tuple 个引用?
How can I construct std::tuple of references from a parameter pack?
我有一个构建器 class,我想将参数存储为参考以供后续构建使用。
我想向我的 class 传递可变数量的参数,使用 class 模板参数推导推断模板参数,并将这些传递的参数作为引用存储在 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};
}
语法错误为:
error: no matching function for call to std::tuple<int&&, double&&>::tuple(int&, double&)
如果只是想参考,直接用:
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&...>
中解析,但后来更简单并提供了解决方案;-)。
另一种方式:
#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)};
}
Builder(Ts&... ts)
是一组左值引用。
Ts{}...
是一组相同类型的纯右值。
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)
这 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) 很有用。
(1) 如果您的 Builder 对象不会超过当前行,并且您不想为移动的对象付费,则很有用。它比(1)更危险。
所有 3 个都将使您的示例代码编译。
我有一个构建器 class,我想将参数存储为参考以供后续构建使用。
我想向我的 class 传递可变数量的参数,使用 class 模板参数推导推断模板参数,并将这些传递的参数作为引用存储在 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};
}
语法错误为:
error: no matching function for call to
std::tuple<int&&, double&&>::tuple(int&, double&)
如果只是想参考,直接用:
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&...>
中解析,但后来更简单并提供了解决方案;-)。
另一种方式:
#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)};
}
Builder(Ts&... ts)
是一组左值引用。
Ts{}...
是一组相同类型的纯右值。
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)
这 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) 很有用。
(1) 如果您的 Builder 对象不会超过当前行,并且您不想为移动的对象付费,则很有用。它比(1)更危险。
所有 3 个都将使您的示例代码编译。