如何从参数包构造引用的 std::元组?
How can I construct std::tuple of references from a parameter pack?
我有一个构建器类,我想将参数存储为参考,以便在后续构建中使用。
我想将可变数量的参数传递给我的类,使用类模板参数推导推断模板参数,并将这些传递的参数作为引用存储在 std::tuple 中。
从参数包转换为 std::元组引用的最简单方法是什么?
我找到了 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&)
没有匹配函数
如果您只是想要引用,请直接使用它们:
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{}...
是同一类型的一组 prvalues。
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 个都将使您的示例代码编译。
- 专用于 std 元组的模板,而无需用户执行remove_cvref
- std::元组分配和复制/移动异常保证
- std::元组和移动语义
- std::元组作为成员替换,方便宏
- 如何从参数包构造引用的 std::元组?
- 用一个额外的元素扩展 std::array 的每个 std::元组
- std::元组大小,是不是错过了优化?
- 初始化语句中临时 std::元组的开销
- 如何改善STD ::元组在空置领域
- 就地解压缩 std::元组,而无需为 std::index_sequence 提供人工层
- 动态创建和扩展STD ::元组为参数包
- 获取std ::元组元素作为std ::变体
- std::元组到元组的映射和使用 emplace
- 检查是否所有 std::元组元素都满足条件 + 设计问题
- Q:std::元组/std::数组
- 为什么GCC警告使用STD ::元组和虚拟继承来调用非平凡的动作分配运算符
- 为什么使用std ::元组和模板派生的类扣除/替换会扣除/替换
- C++ 如何根据部分特化制作 std::元组类型?
- 由std ::元组存储的垃圾价值
- 使用 C++11 std::元组在大型项目中