C++ 生成一个副本作为接受右值引用的函数的输入

C++ 生成一个副本作为接受右值引用的函数的输入,c++,c++14,rvalue-reference,C++,C++14,Rvalue Reference,我有一个库函数(此处不受我的控制),它将r值引用到可移动和可复制的类型栏: void foo(Bar&& b); 在我自己的代码中,有时需要给它一个现有值的副本,例如 const auto b = getBar(); foo(mk_copy(b)); b.baz(); 这就是我想到的 template<typename T> auto mk_copy(T val){return val;} 但我不想重复类型名称 没有标准的机制,但是如果你想要,你的例子不是很好。

我有一个库函数(此处不受我的控制),它将r值引用到可移动和可复制的类型栏:

void foo(Bar&& b);
在我自己的代码中,有时需要给它一个现有值的副本,例如

const auto b = getBar();
foo(mk_copy(b));
b.baz();
这就是我想到的

template<typename T> auto mk_copy(T val){return val;}

但我不想重复类型名称

没有标准的机制,但是如果你想要,你的例子不是很好。第一个
mk_copy
复制
T
两次(或复制和移动)。第二个似乎很困惑,因为它试图做什么

显而易见的方法是简单地使用
常量&
,就像您通常会做的那样:

template<typename T> T mk_copy(const T &val){return val;}
template T mk_copy(const T&val){return val;}

对于内置类型,前缀
+
起到“复制”的作用:

void foo( char&& ) {}
void bar( double*&& ) {}

auto main() -> int
{
    char x{};
    foo( +x );

    double* p{};
    bar( +p );
}
不过,如果您将前缀
++
应用于其他类型,代码读者可能会感到困惑,并且通用前缀
运算符+
模板可能最终与某些类自己的前缀
运算符+
冲突

因此,我建议使用现在明显的制造商命名惯例,即
make
前缀

然后它可以像这样:

template< class Type >
auto make_copy( Type const& o ) -> Type { return o; }
模板
自动复制(类型const&o)->类型{return o;}

你提出的第一个解决方案

template<typename T> auto mk_copy(T val){return val;}
template<typename T> auto mk_copy(T&& val){return std::forward<T>(val);}
template auto mk_copy(T val){return val;}
可能会复制值两次:首先复制到by value参数,然后复制到函数结果

对于可移动类型(如标准库容器),这不是问题,因为返回类型复制将减少为移动。但对于较大的不可移动类型来说,这可能是一个问题,就像在遗留代码中一样


第二个提议的解决方案

template<typename T> auto mk_copy(T val){return val;}
template<typename T> auto mk_copy(T&& val){return std::forward<T>(val);}
template auto mk_copy(T&&val){return std::forward(val);}

通过转发引用(也称为通用引用)获取参数,然后通过重新创建参数类型推断返回类型。返回类型将始终是非引用,因为这是纯
auto
推断的,所以这在技术上是正确的。但是它不必要地复杂。

我认为您最好定义
mk\u copy
方法,或者添加一个声明:

auto b_copy = b;
foo(std::move(b_copy));
这些对读者来说更清楚


为了演示这些可能性,您可以使用
decltype
获取类型:

foo(std::decay_t<decltype(b)>{b});
或元组:

foo(std::get<0>(std::make_tuple(b)));

直接调用复制构造函数怎么样<代码>foo(Bar{b})
make_copy
似乎是最干净的。前缀
make
似乎正在成为这类事情的惯例。为什么不能在与实际库函数(内部复制并移动到实际函数)相同的名称空间中为左值编写一个重载
foo
因此,调用代码可以看起来非常自然,没有
mk\u copy
噪音?这本质上是
decation\u copy
,我们可能有一天会得到,也可能没有。那张纸好像一直在忙着从缝里掉下来。@T.C.酷,谢谢。RVO不是申请了第一张吗?@songyuanyao:不,谢谢。第一,复制和移动是否没有?第二个应该是一个单一的副本,但优化到一个单一的移动,如果可能的话。有点过分,但这是错的吗?我承认我没有想清楚。@JohanLundberg:一份拷贝后跟着一份拷贝/移动,这取决于
t
能做什么。重温:“第二份似乎很难理解它想做什么”。正如T.C.所指出的,这与decade_copy相同(减去一些条件),.decltype(b){b},如果b是一个引用,则推断出一个引用,因此这不起作用。@JohanLundberg添加了
std::decade
。Ok:)。另外,我认为使用{}调用副本构造函数也有缺点。(该类可能有一个初始值设定项列表构造函数执行完全不同的操作..)
foo(std::get<0>(std::make_tuple(b)));
foo(std::make_pair(b, 0).first);