pat*_*oft 7 c++ concurrency stl thread-safety
考虑到以下简化代码,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;
}
Run Code Online (Sandbox Code Playgroud)
正如我所看到的那样,我的意图是并发性,并且在释放互斥锁之后,不能保证映射值的继续存在.
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];
}
Run Code Online (Sandbox Code Playgroud)
都会返回副本。不同之处在于,在第一种情况下,编译器很可能会使用复制省略/RVO(即不调用复制构造函数),而在第二种情况下,它必须调用复制构造函数并进行复制。
如果您的编译器没有使用复制省略/RVO,则根据 C++11 标准,返回值是临时值(在第一种情况下),并且由于类 std::string 是可移动的,因此临时值将被移动。例如,
std::string newStr = myFunction(); // RHS returns an r-value => move-semantics is used
Run Code Online (Sandbox Code Playgroud)
因此,事先判断是否会使用移动语义或是否会发生复制省略/RVO 并不总是显而易见的,这取决于您的编译器。如果你愿意的话,你可以强制移动语义,通过使用
std::move
Run Code Online (Sandbox Code Playgroud)
编辑:顺便说一句,您甚至不被允许返回对临时对象的引用。您不能获取右值(临时)的引用。