转发具有明确类型的引用行为
Forwarding reference behavior with definite types
假设我有一个模板class
template <typename T> class foo;
template <typename... Args>
struct foo<std::tuple<Args...>> {
std::tuple<Args...> t;
foo(Args&&... args): t{std::forward<Args>(args)...} { }
};
我知道在这种情况下 Args&&...
是右值引用,我也可以写 std::move
而不是 std::forward
。
我也可以有一个带有左值引用的构造函数,就像这样
foo(const Args&... args): t{args...} { }
问题是是否有可能获得与转发引用相同的行为,但对于确定的类型?我想要这个的原因是我可以使用像
这样的语法
foo bar({. . .}, {. . .}, . . ., {. . .});
如果我定义 foo(Args&&... args)
构造函数,这会起作用,但不允许混合场景,在这种情况下,我想用大括号括起来的初始化列表初始化一些成员元组元素,并从中复制其他元素预先存在的对象实例。
当然;有一个奇特而简单的方法。
我将在下面详细介绍的奇特方式。首先是简单的方法:按值获取。
template <typename... Args>
struct foo<std::tuple<Args...>> {
std::tuple<Args...> t;
foo(Args... args): t{std::forward<Args>(args)...} { }
};
真的,就这样做吧。如果 Args
包含引用,则转发用于做正确的事情。
按值获取比完美转发增加了一步,但以指数方式减少了对过载的要求。
这是奇特的方式。我们键入擦除结构:
template<class T>
struct make_it {
using maker=T(*)(void*);
maker f;
void* args;
// make from move
make_it( T&& t ):
f([](void* pvoid)->T{
return std::move(*static_cast<T*>(pvoid));
}),
args(std::addressof(t))
{}
// make from copy
make_it( T const& t ):
f([](void* pvoid)->T{
return *(T const*)(pvoid);
}),
args(std::addressof(t))
{}
operator T()&&{return std::move(*this)();}
T operator()()&&{ return f(args); }
};
此类型通过复制或移动擦除构造。
template <typename... Args>
struct foo<std::tuple<Args...>> {
std::tuple<Args...> t;
foo(make_it<Args>... args): t{std::move(args)()...} { }
};
它不是完全透明的,但它是我能得到的最接近的。
需要双人 {{}}
而不是单人。它是用户定义的转换,因此不会隐式完成另一个转换。我们可以添加一个通用的 ctor:'
// make from universal
template<class U>
make_it( U&& u ):
f([](void* pvoid)->T{
return std::forward<U>(*(U*)(pvoid));
}),
args(std::addressof(u))
{}
如果我们添加一个 U&&
可用于隐式构造 T
.
的 sfinae 奶嘴,效果会更好
它有一些优点,但与仅按值取值相比,它们是微不足道的。例如,在 C++17 中,不可移动类型在某些情况下可以完全前向构造。
假设我有一个模板class
template <typename T> class foo;
template <typename... Args>
struct foo<std::tuple<Args...>> {
std::tuple<Args...> t;
foo(Args&&... args): t{std::forward<Args>(args)...} { }
};
我知道在这种情况下 Args&&...
是右值引用,我也可以写 std::move
而不是 std::forward
。
我也可以有一个带有左值引用的构造函数,就像这样
foo(const Args&... args): t{args...} { }
问题是是否有可能获得与转发引用相同的行为,但对于确定的类型?我想要这个的原因是我可以使用像
这样的语法foo bar({. . .}, {. . .}, . . ., {. . .});
如果我定义 foo(Args&&... args)
构造函数,这会起作用,但不允许混合场景,在这种情况下,我想用大括号括起来的初始化列表初始化一些成员元组元素,并从中复制其他元素预先存在的对象实例。
当然;有一个奇特而简单的方法。
我将在下面详细介绍的奇特方式。首先是简单的方法:按值获取。
template <typename... Args>
struct foo<std::tuple<Args...>> {
std::tuple<Args...> t;
foo(Args... args): t{std::forward<Args>(args)...} { }
};
真的,就这样做吧。如果 Args
包含引用,则转发用于做正确的事情。
按值获取比完美转发增加了一步,但以指数方式减少了对过载的要求。
这是奇特的方式。我们键入擦除结构:
template<class T>
struct make_it {
using maker=T(*)(void*);
maker f;
void* args;
// make from move
make_it( T&& t ):
f([](void* pvoid)->T{
return std::move(*static_cast<T*>(pvoid));
}),
args(std::addressof(t))
{}
// make from copy
make_it( T const& t ):
f([](void* pvoid)->T{
return *(T const*)(pvoid);
}),
args(std::addressof(t))
{}
operator T()&&{return std::move(*this)();}
T operator()()&&{ return f(args); }
};
此类型通过复制或移动擦除构造。
template <typename... Args>
struct foo<std::tuple<Args...>> {
std::tuple<Args...> t;
foo(make_it<Args>... args): t{std::move(args)()...} { }
};
它不是完全透明的,但它是我能得到的最接近的。
需要双人 {{}}
而不是单人。它是用户定义的转换,因此不会隐式完成另一个转换。我们可以添加一个通用的 ctor:'
// make from universal
template<class U>
make_it( U&& u ):
f([](void* pvoid)->T{
return std::forward<U>(*(U*)(pvoid));
}),
args(std::addressof(u))
{}
如果我们添加一个 U&&
可用于隐式构造 T
.
它有一些优点,但与仅按值取值相比,它们是微不足道的。例如,在 C++17 中,不可移动类型在某些情况下可以完全前向构造。