C++ RVO:返回通过值传递的值,即使显式分配给常量引用

C++ RVO:返回通过值传递的值,即使显式分配给常量引用,c++,caching,boost,rvo,C++,Caching,Boost,Rvo,我有一个设置框架,它最终将存储在boost::any的std::map中的值缓存起来 因为我不希望客户端处理异常,所以它提供了一个默认值,设置框架在设置检索失败时会回退:这迫使我通过复制返回设置值 class SettingsMgr { public: template<class T> T getSetting(const std::string& settingName, const T& settingDefValue)

我有一个设置框架,它最终将存储在
boost::any
std::map
中的值缓存起来

因为我不希望客户端处理异常,所以它提供了一个默认值,设置框架在设置检索失败时会回退:这迫使我通过复制返回设置值

class SettingsMgr
{

    public:
        template<class T>
        T getSetting(const std::string& settingName, const T& settingDefValue)
        {
            try
            {
                if(cache.find(settingName) != cache.end)
                {
                     return any_cast<const T&>(cache.find(settingName)->second);
                }
                else
                {
                    cache[settingName] = someDbRetrievalFunction<T>(settingName);    
                    return any_cast<const T&>(cache.find(settingName)->second);
                }
             }
             catch(...)
             {
                 return settingDefValue;
             }
          }
        // This won't work in case the default value needs to be returned 
        // because it would be a reference to a value the client - and not the SettingsMgr - 
        // owns (which might be temporary etc etc)
        template<class T>
        const T& getSettingByRef(const std::string& settingName, const T& settingDefValue);

     private:
        std::map<std::string, boost::any> cache;
}
类设置mgr
{
公众:
模板
T getSetting(常数std::string&settingName,常数T&settingDefValue)
{
尝试
{
if(cache.find(settingName)!=cache.end)
{
返回任意_cast(cache.find(settingName)->second);
}
其他的
{
cache[settingName]=someDbRetrievalFunction(settingName);
返回任意_cast(cache.find(settingName)->second);
}
}
捕获(…)
{
返回settingDefValue;
}
}
//如果需要返回默认值,则此操作无效
//因为它是对客户端的值的引用,而不是对setingsMgr的引用
//拥有(可能是临时的等)
模板
const T&getSettingByRef(const std::string&settingName,const T&settingDefValue);
私人:
地图缓存;
}
现在,我并不认为这有什么大不了的,因为我认为多亏了RVO magic,对设置框架拥有的缓存值的引用会被重新引用——特别是当客户端显式地将返回值封装在常量引用中时

根据我的测试,情况似乎并非如此

void main() {
    SettingsMgr sm;
    // Assuming everything goes fine, SNAME is cached
    const std::string& asettingvalue1 = sm.getSetting<std::string>("SNAME", "DEF_VALUE"); 

    // Assuming everything goes fine, cached version is returned (no DB lookup)
    const std::string& asettingvalue2 = sm.getSetting<std::string>("SNAME", "DEF_VALUE");

    ASSERT_TRUE(&asettingvalue1 == &asettingvalue2);  // Fails

    const std::string& awrongsettingname = sm.getSettingByRef<std::string>("WRONGSETTINGNAME", "DEF_VALUE");
    ASSERT_TRUE(awrongsettingname  == "DEF_VALUE"); // Fails, awrongsettingname is random memory
}
void main(){
设置SMGR sm;
//假设一切顺利,SNAME将被缓存
const std::string&asettingvalue1=sm.getSetting(“SNAME”、“DEF_值”);
//假设一切正常,则返回缓存版本(无数据库查找)
const std::string&asettingvalue2=sm.getSetting(“SNAME”、“DEF_值”);
ASSERT_TRUE(&asettingvalue1==&asettingvalue2);//失败
const std::string&awrongsettingname=sm.getSettingByRef(“错误设置名称”、“定义值”);
ASSERT_TRUE(awrongsettingname==“DEF_VALUE”);//失败,awrongsettingname是随机内存
}

您可以使用
getSettingByRef
版本,防止传递任何右值引用:

    template<class T>
    const T & getSetting(const std::string& settingName, T&& settingDefValue) {
         static_assert(false, "No rvalue references allowed!");
    }
模板
const T&getSetting(const std::string&settingName,T&settingDefValue){
静态_断言(false,“不允许右值引用!”);
}

您的函数在做什么?请尝试创建并向我们展示。另外,您是否尝试在调试器中单步执行代码,以便知道实际调用了哪个函数?您正在尝试帮助编译器完成其工作。IIRC RVO仅在将结果分配给一个值时才会生效。
getSetting
按值返回,它返回的每个值都将是一个不同的对象,无论RVO是否消除了在函数体中使用第二个对象的需要。提供方法
T getSettingRef如何(const std::string&settingName,const T&settingDefValue)
T getSettingRef(const std::string&settingName,T&&settingDefValue)=delete;
,因此您不能将其与临时值一起使用。(用户仍需保证默认值的生存期长于返回值)@codeJack你的意思是这是一个性能优化?你确定这不是过早的优化吗?你测量过内存使用情况并确定它很重要吗?按照@Jarod42的建议将其
=delete
,会在编译时出错?这个问题应该在编译时识别,而不是在运行时。我选择了静态断言向用户提供解释性消息。在此之前,我无意中抛出了一个。