C++ 通用引用参数使用两次
我希望围绕std::make_对创建一个包装器,它接受一个参数并使用该参数生成该对的第一个和第二个成员。此外,我希望利用移动语义 我们可能会天真地编写(为了清晰起见,忽略返回类型)C++ 通用引用参数使用两次,c++,c++11,perfect-forwarding,C++,C++11,Perfect Forwarding,我希望围绕std::make_对创建一个包装器,它接受一个参数并使用该参数生成该对的第一个和第二个成员。此外,我希望利用移动语义 我们可能会天真地编写(为了清晰起见,忽略返回类型) 模板 无效foo(T&T) { std::make_pair(std::forward(t), 标准:正向(t)); } 但这不太可能达到我们的目的 我们想要的是: 在使用(const)左值引用参数调用foo的情况下,我们应该将(const)引用传递给std::make_pair,而不修改这两个参数 在使用右值引
模板
无效foo(T&T)
{
std::make_pair(std::forward(t),
标准:正向(t));
}
但这不太可能达到我们的目的
我们想要的是:
- 在使用(const)左值引用参数调用foo的情况下,我们应该将(const)引用传递给std::make_pair,而不修改这两个参数
- 在使用右值引用参数调用foo的情况下,我们应该复制被引用的对象,然后使用原始右值引用以及新创建的对象的右值引用调用std::make_pair
template <typename T>
T forward_or_duplicate(T t)
{
return t;
}
template <typename T>
void foo(T&& t)
{
std::make_pair(std::forward<T>(t),
forward_or_duplicate<T>(t));
}
模板
T向前或重复(T T)
{
返回t;
}
模板
无效foo(T&T)
{
std::make_pair(std::forward(t),
转发或复制(t);
}
但我有理由肯定这是错的
因此,问题是:
foo
中的t
是一个左值,因此构造通过值传递给的t
fromt
调用复制构造函数
即使它确实有效,它是最优的吗?同样,我怀疑在从返回T时不会调用T的复制构造函数
转发或复制()
否,t
是一个函数参数,因此返回隐式移动,不复制
也就是说,此版本将更加高效和安全:
template <typename T>
T forward_or_duplicate(std::remove_reference_t<T>& t)
{
return t;
}
模板
转发或复制(标准:删除引用)
{
返回t;
}
如果T
是左值引用,则会产生与前面相同的签名。如果T
不是参考,这将为您节省移动时间。此外,它将T
放入一个非推断上下文中,这样您就不会忘记指定它。您的确切代码可以工作。它的细微变化(即,不调用make_pair
,而是调用一些其他函数)会导致未指定的结果。即使它看起来有效,远离这行代码(本地正确)的细微更改也会破坏它
您的解决方案不是最优的,因为它可以将t
复制两次,即使它可以工作,但只需要复制一次
这是迄今为止最简单的解决方案。它不会修复其他地方的代码更改导致的细微中断,但如果您真的要调用make\u pair
,则不必担心:
template <typename T>
void foo(T&& t) {
std::make_pair(std::forward<T>(t),
static_cast<T>(t));
}
但这阻碍了一个理论上的省略机会(这里没有)
通常,在与static\u cast(t)
相同的函数调用上调用std::forward(t)
或任何等效的复制或转发函数都是一个坏主意。未指定参数求值的顺序,因此如果使用std::forward(t)
的参数不是t&
类型,并且其构造函数看到右值t
并将状态移出,则静态转换(t)
可以在t
的状态被移出后求值
这里不会发生这种情况:
template <typename T>
void foo(T&& t) {
T t2 = t;
std::make_pair(std::forward<T>(t),
std::forward<T>(t2));
}
模板
无效foo(T&T){
t2=T;
std::make_pair(std::forward(t),
标准:正向(t2));
}
因为我们将复制或向前移动到另一行,在那里初始化t2
而t2=T如果T&
是左值引用,T
也是左值引用,int&t2=T代码>不复制。我认为如果定义了参数求值之间的顺序(不幸的是,它没有定义),您只需使用std::make_pair(t,std::forward(t))
就可以做到这一点。@KerrekSB:Hah,没有想到这一点。你应该提交一个答案:-)我实际上没有发现转发或复制@KerrekSB有任何问题,我没有看到make_-pair
通过引用获取参数,因此移动发生在它内部,复制发生在您输入make_-pair
@KerrekSB之前,它转发左值,复制右值。谢谢。这回答了我最初的问题。在我最初的解决方案中,我使用了“T forward_或_duplicate(typename std::enable_if::type T)”将T放入一个非推断上下文中,但为了清晰起见,在问题中省略了它。
template <typename T>
void foo(T&& t) {
T t2 = t;
std::make_pair(std::forward<T>(t),
std::forward<T>(t2));
}
template <typename T>
void foo(T&& t) {
T t2 = t;
std::make_pair(std::forward<T>(t),
std::forward<T>(t2));
}