【问题标题】:C++ copy elision for references引用的 C++ 复制省略
【发布时间】:2015-07-13 08:35:16
【问题描述】:

考虑下面的简化代码,Cache::operator[] 的调用者保证会收到映射值的副本

#include <string>
#include <map>
#include <mutex>
#include <iostream>

class Cache {
    public:
        std::string operator[] (int k) {
            std::lock_guard<std::mutex> lock(m_mutex);

            if (! m_map.count(k)) m_map[k] = "Hello world";
            return m_map[k];
        }

    private:
        std::mutex m_mutex;
        std::map<int, std::string> m_map;
};

int main (int argc, char *argv[]) {
    Cache c;
    auto v = c[42];
    std::cout << v << std::endl;
    return 0;
}

正如我看到的那样,我的意图是并发,并且在互斥锁释放后,映射值的继续存在并不能保证。

std::map&lt;&gt;::operator[] 返回引用 std::string&amp;。我的理解是复制构造会产生一个无名的临时性,然后可能会受到 RVO 的约束。

复制省略何时会发生,这是否会导致不同的线程返回相同的对象而不是它们自己的副本?如果可以,如何避免?

实际代码涉及数据库查找填充缓存,其中映射键是表主键,映射值是从行字段构造的对象。

【问题讨论】:

  • @inf 谢谢。因此,如果我理解正确更改 Cache::operator[] (int) 以返回引用 (std::string&amp;) 会破坏所需的并发性?
  • 是的,因为这样用户可以通过引用而不是副本直接访问。

标签: c++ concurrency stl thread-safety


【解决方案1】:

您拥有的代码很好。当编译器意识到它可以优化掉临时对象并改为在适当位置构造新对象时,就会发生复制省略。 map::operator[] 返回对其值类型的引用这一事实无关紧要,该函数没有返回引用。因此,

// case 1
std::string myFunction()
{
    return std::string("Hello");
}

// case 2
std::string myFunction(int k)
{
    return m_map[k];
}

都将返回副本。不同之处在于,在第一种情况下,您的编译器很可能会使用复制省略/RVO(即不调用复制构造函数),而在第二种情况下,它必须调用复制构造函数并进行复制。

如果你的编译器没有使用复制省略/RVO,那么根据 C++11 标准,返回值是一个临时值(在第一种情况下),并且由于类 std::string 是可移动的,所以临时值是感动。例如,

std::string newStr = myFunction(); // RHS returns an r-value => move-semantics is used

因此,事先说明是否会使用移动语义或是否会发生复制省略/RVO 并不总是很明显,这取决于您的编译器。如果你愿意,你可以强制移动语义,通过使用

std::move

编辑:顺便说一句,您甚至不能返回对临时对象的引用。您不能引用 r 值(临时)。

【讨论】:

    【解决方案2】:

    复制省略只是为了避免在处理临时对象时不必要的复制而实施的编译器优化。在您的情况下,您不是返回临时值,而是返回成员变量的值。由于您按值返回它,因此它将被复制。没有办法解决。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-14
      • 1970-01-01
      • 2014-09-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多