按值传递和按右值引用传递的重载
Overloading for pass-by-value and pass-by-rvalue-reference
我有一个子例程的两个重载,这些子例程采用占用几兆字节动态内存的类型的参数并具有移动构造函数和赋值运算符:
// 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);
}
显然,第一个重载将 newParameter 的内容从 newParameter 对象起源的子例程链上游移动,而第二个重载创建 newParameter 的全新副本(或调用复制省略以避免在适当的地方这样做,例如参数实际上是函数的 return 值),然后将副本移动到本地数据成员中,从而避免进一步复制。
但是,如果我实际尝试使用第一个重载将对象移动到我的 class 中:
{
MyClass theDestination;
MyMoveableType theObject
...
// ...Various actions which populate theObject...
...
TheDestination.setParameter(std::move(theObject));
...
}
...然后在我尝试过的每个编译器上,我都会收到如下错误:
call to member function 'setParameter' is ambiguous
现在我可以看到,将右值引用传递给第二个重载实际上是完全合法的,如果我没有提供第一个重载,这也是我希望编译器在不发出警告的情况下执行的操作超载。即便如此,我希望编译器能够非常清楚这段代码的意图,因此我希望它 select 第二次重载是最佳匹配。
我可以通过重新定义第二个构造函数以获取 const 引用并取消 std::move 来消除错误(尽管它不会是一个错误
留在里面;编译器会忽略它)。这可以正常工作,但我会失去利用复制省略的机会。这可能是
对于此特定应用程序的性能方面很重要;正在讨论的对象是 30 帧流过的高分辨率视频帧
每秒帧数。
在这种情况下,我能做些什么来消除重载的歧义,从而让我的例程同时具有按值传递和按右值传递版本?
The intention is clearly that the first overload moves the contents of newParameter from wherever up the chain of subroutine-calls the newParameter object originated, whilst the second overload creates a brand new copy
这不是你真正的做法。你有两个理智的选择:
方法 A
您只写 值重载 然后无论如何都从它移动 - 这意味着您将始终支付构造函数价格,无论是移动还是复制。
方法 B
您为 (const T&)
和 (T&&)
编写重载。这样你就可以复制第一个并使用完美转发跳过第二个中的移动 CTOR。
我建议将方法 A 作为默认方法,而仅当 c-tor 调用确实很重要时才推荐方法 B。
我有一个子例程的两个重载,这些子例程采用占用几兆字节动态内存的类型的参数并具有移动构造函数和赋值运算符:
// 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);
}
显然,第一个重载将 newParameter 的内容从 newParameter 对象起源的子例程链上游移动,而第二个重载创建 newParameter 的全新副本(或调用复制省略以避免在适当的地方这样做,例如参数实际上是函数的 return 值),然后将副本移动到本地数据成员中,从而避免进一步复制。
但是,如果我实际尝试使用第一个重载将对象移动到我的 class 中:
{
MyClass theDestination;
MyMoveableType theObject
...
// ...Various actions which populate theObject...
...
TheDestination.setParameter(std::move(theObject));
...
}
...然后在我尝试过的每个编译器上,我都会收到如下错误:
call to member function 'setParameter' is ambiguous
现在我可以看到,将右值引用传递给第二个重载实际上是完全合法的,如果我没有提供第一个重载,这也是我希望编译器在不发出警告的情况下执行的操作超载。即便如此,我希望编译器能够非常清楚这段代码的意图,因此我希望它 select 第二次重载是最佳匹配。
我可以通过重新定义第二个构造函数以获取 const 引用并取消 std::move 来消除错误(尽管它不会是一个错误 留在里面;编译器会忽略它)。这可以正常工作,但我会失去利用复制省略的机会。这可能是 对于此特定应用程序的性能方面很重要;正在讨论的对象是 30 帧流过的高分辨率视频帧 每秒帧数。
在这种情况下,我能做些什么来消除重载的歧义,从而让我的例程同时具有按值传递和按右值传递版本?
The intention is clearly that the first overload moves the contents of newParameter from wherever up the chain of subroutine-calls the newParameter object originated, whilst the second overload creates a brand new copy
这不是你真正的做法。你有两个理智的选择:
方法 A
您只写 值重载 然后无论如何都从它移动 - 这意味着您将始终支付构造函数价格,无论是移动还是复制。
方法 B
您为 (const T&)
和 (T&&)
编写重载。这样你就可以复制第一个并使用完美转发跳过第二个中的移动 CTOR。
我建议将方法 A 作为默认方法,而仅当 c-tor 调用确实很重要时才推荐方法 B。