首页 > 解决方案 > 为什么我们不能使用 move 语义只适用于 pass-by-value 而不是 const lvalue 引用?

问题描述

是我用作参考的文章,最终在此 SO answer中提到。

作者举了两个例子:

示例 1:

std::vector<std::string> 
sorted(std::vector<std::string> names)
{
    std::sort(names);
    return names;
}

// names is an lvalue; a copy is required so we don't modify names
std::vector<std::string> sorted_names1 = sorted( names );

// get_names() is an rvalue expression; we can omit the copy!
std::vector<std::string> sorted_names2 = sorted( get_names() );

示例 2:

std::vector<std::string> 
sorted2(std::vector<std::string> const& names) // names passed by reference
{
    std::vector<std::string> r(names);        // and explicitly copied
    std::sort(r);
    return r;
}

然后说:

尽管 sorted 和 sorted2 乍一看似乎是相同的,但如果编译器确实进行了复制省略,则可能存在巨大的性能差异。即使 sorted2 的实际参数是一个右值,副本的来源 names 也是一个左值,所以副本不能被优化掉。从某种意义上说,复制省略是分离编译模型的牺牲品:在 sorted2 的主体内部,没有关于函数的实际参数是否为右值的信息;在外面,在呼叫现场,没有迹象表明最终会制作一份论据的副本。

我的问题很简单:为什么编译器不能在第二个示例中使用复制省略,但它可以在第一个示例中使用?

值传递和引用如何区分它们?names在两个实例中都被命名,所以我假设我们也在两个实例中创建了一个左值。

标签: c++move-semanticsrvalue

解决方案


不同之处在于names可修改对象与否。const 左值引用不可修改。


由于 get_names() 是一个右值,我们可以使用移动语义。

不总是。考虑何时调用移动构造函数。

但是,如果参数是 const 左值引用,那么我们必须将值“保留”为左值,因此不能发生简单的指针切换

当参数为左值引用时,可以修改对象。但是,您不能传递右值。

std::vector<std::string> 
sorted3(std::vector<std::string>& names)
{
    std::sort(names);
    return names;
}

// names is an lvalue; lvalue reference can bind lvalue. argument will be modified after execution `sorted3`.
std::vector<std::string> sorted_names1 = sorted3( names );

// get_names() is an rvalue expression; we cannot bind by lvalue reference.
//std::vector<std::string> sorted_names2 = sorted3( get_names() );

const 左值引用可以绑定右值。但是,您不能修改该对象,从而不能发生简单的指针切换。


推荐阅读