考虑到以下简化代码,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<> :: operator []返回一个引用std :: string&.我的理解是复制结构会产生一个无名的临时版,然后可能会受到RVO的影响.
何时会发生复制省略并且这会导致不同的线程返回相同的对象而不是自己的副本?如果是这样,怎么能避免这种情况?
解决方法
你有的代码很好.当编译器意识到它可以优化掉临时对象而不是在适当的位置构造新对象时,就会发生复制省略. map :: operator []返回对其值类型的引用这一事实无关紧要,该函数不返回引用.因此,
// case 1 std::string myFunction() { return std::string("Hello"); } // case 2 std::string myFunction(int k) { return m_map[k]; }
都将返回副本.不同之处在于,在第一种情况下,您的编译器很可能使用copy elision / RVO(即不调用复制构造函数),而在第二种情况下,它必须调用复制构造函数并进行复制.
如果您的编译器不使用copy elision / RVO,而不是C 11标准,则返回的值是临时的(在第一种情况下),并且因为类std :: string是可移动的,所以临时将被移动.例如,
std::string newStr = myFunction(); // RHS returns an r-value => move-semantics is used
因此,预先说明是否使用move-semantics或者是否会出现copy elision / RVO并不总是很明显,这取决于你的编译器.如果您愿意,可以使用强制移动语义
std::move
编辑:顺便说一句,你甚至不会被允许返回对临时的引用.你不能参考r值(临时).