重载按值传递和按右值引用传递

Eos*_*ern 5 parameter-passing rvalue-reference move-semantics c++11

我有两个子例程重载,该子例程采用占用几兆动态内存的类型参数,并具有移动构造函数和赋值运算符:

// Version intended for use when we the caller has 
// deliberately passed an rvalue reference using std::move
void MyClass::setParameter(MyMoveableType &&newParameter)
{
    m_theLocalParameter = std::move(newParameter);
}

// Version intended for use when the caller has passed
// some other type of value which shouldn't be moved
void MyClass::setParameter(MyMoveableType newParameter) 
{
    m_theLocalParameter = std::move(newParameter);
}
Run Code Online (Sandbox Code Playgroud)

其意图显然是,第一个重载将 newParameter 的内容从子例程链上的任何地方移动 - 调用 newParameter 对象,而第二个重载创建 newParameter 的全新副本(或调用复制省略以避免在适当的情况下这样做,例如,参数实际上是函数的返回值),然后将副本移动到本地数据成员中,从而避免进一步的复制。

但是,如果我尝试使用第一个重载实际将对象移动到我的类中:

{
    MyClass        theDestination;
    MyMoveableType theObject
    ...
    // ...Various actions which populate theObject...
    ...

    TheDestination.setParameter(std::move(theObject));
    ...
}
Run Code Online (Sandbox Code Playgroud)

...然后在我尝试过的每个编译器上都会出现以下错误:

call to member function 'setParameter' is ambiguous
Run Code Online (Sandbox Code Playgroud)

现在我可以看到,如果我没有提供第一个重载,则将右值引用传递给第二个重载实际上是完全合法的,并且是我期望编译器执行的操作,而不会发出警告。即便如此,我希望编译器能够完全清楚这段代码的意图是什么,因此我希望它会选择第二个重载作为最佳匹配。

我可以通过重新定义第二个构造函数以采用 const 引用并消除 std::move 来消除错误(尽管将其保留在其中不会是错误;编译器只会忽略它)。这可以正常工作,但我会失去利用复制省略的机会。对于该特定应用程序来说,这对于性能而言可能非常重要;所讨论的对象是每秒 30 帧的高分辨率视频帧。

在这种情况下,我可以做些什么来消除重载的歧义,从而使我的例程同时具有按值传递和按右值引用传递版本吗?

Bar*_*icz 0

其意图显然是,第一个重载将 newParameter 的内容从子例程链上的任意位置移动 - 调用 newParameter 对象,而第二个重载则创建一个全新的副本

这并不是你真正的做法。你有两个明智的选择:

方法A

只需编写值重载,然后无论如何都可以从中移动 - 这意味着您将始终付出构造函数的代价,无论是移动还是复制。

方法B

(const T&)您为和编写重载(T&&)。这样,您可以复制第一个,并使用完美转发跳过第二个中的移动 CTOR。


我建议将方法 A 作为默认方法,仅当 c-tor 调用实际上很重要时才使用方法 B。