【问题标题】:RVO: Return value passed by value even if explicitly assigned to a const referenceRVO:即使显式分配给 const 引用,也按值传递返回值
【发布时间】:2017-06-20 08:14:41
【问题描述】:

我有一个设置框架,它最终会将值存储到 std::mapboost::any 中。

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

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;
}

现在,我并不认为这有什么大不了的,因为我认为多亏了 RVO 魔术,对设置框架所拥有的缓存值的引用会被返回 - 特别是当客户端显式地将返回值封装在一个常量引用!

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

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
}

【问题讨论】:

  • 你的函数在做什么?请尝试创建Minimal, Complete, and Verifiable Example 并向我们展示。另外,您是否尝试过在调试器中单步执行代码以便知道实际调用了哪个函数?
  • 你正试图帮助编译器完成它的工作。仅当您将结果分配给一个值时,IIRC RVO 才会生效。
  • getSetting 按值返回,它返回的每个值都将是一个不同的对象,无论 RVO 是否消除了函数体内对第二个对象的需要。
  • 如何提供方法T getSettingRef(const std::string&amp; settingName, const T&amp; settingDefValue)T getSettingRef(const std::string&amp; settingName, T&amp;&amp; settingDefValue) = delete;,所以你不能临时使用它。 (用户仍然要保证默认值的生命周期长于返回的生命周期)。
  • @codeJack 你的意思是这是一个性能优化?你确定这不是过早的优化吗?您是否测量了内存使用量并确定它很重要?

标签: c++ caching boost rvo


【解决方案1】:

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

    template<class T>
    const T & getSetting(const std::string& settingName, T&& settingDefValue) {
         static_assert(false, "No rvalue references allowed!");
    }

【讨论】:

  • 按照@Jarod42 的建议输入= delete 会在编译时引发错误吗?这个问题应该在编译时发现,而不是在运行时。
  • 我选择了静态断言来给用户一个解释性的信息。在我不小心扔到那里之前。
猜你喜欢
  • 1970-01-01
  • 2019-08-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-09
  • 2020-11-07
  • 1970-01-01
相关资源
最近更新 更多