【问题标题】:disallow passing of rvalue reference to a function禁止将右值引用传递给函数
【发布时间】:2015-02-26 10:30:01
【问题描述】:

我们有以下从地图中获取值的便利函数 如果未找到键,则返回后备默认值。

template <class Collection> const typename Collection::value_type::second_type&
    FindWithDefault(const Collection& collection,
                    const typename Collection::value_type::first_type& key,
                    const typename Collection::value_type::second_type& value) {
      typename Collection::const_iterator it = collection.find(key);
      if (it == collection.end()) {
        return value;
      }
      return it->second;
    }

这个函数的问题是它允许传递一个临时对象作为第三个参数,这将是一个错误。例如:

const string& foo = FindWithDefault(my_map, "");

是否可以通过使用以某种方式禁止将右值引用传递给第三个参数 std::is_rvalue_reference 和静态断言?​​

【问题讨论】:

  • 在那里传递"" 感觉非常直观。最好找到一种方法来防止FindWithDefault 被绑定到引用,除非我认为没有。无论如何,在本例中,最好简单地将 foo 设为一个值。
  • 这个问题的另一个问题是你已经跳到你认为的解决方案,而不是陈述问题。这样做时,您专注于右值引用,但这不是唯一的问题;对-const-temporary 的左值引用会同样难受。也许按值取默认的value
  • 您可以按值返回,而不是按引用返回。对于默认查找而言,这似乎很自然。
  • 如果您不允许传递右值,用户将创建一个变量并传递它。他不会被警告返回值是对其变量的引用。这看起来很危险且有缺陷,除非你在课程文档中写了一个大的红色警告

标签: c++ c++11 stl


【解决方案1】:

添加这个额外的重载应该可以工作(未经测试):

template <class Collection>
const typename Collection::value_type::second_type&
FindWithDefault(const Collection& collection,
                const typename Collection::value_type::first_type& key,
                const typename Collection::value_type::second_type&& value) = delete;

重载解析将为右值引用选择此重载,= delete 使其成为编译时错误。或者,如果你想指定一个自定义消息,你可以去

template <class Collection>
const typename Collection::value_type::second_type&
FindWithDefault(const Collection& collection,
                const typename Collection::value_type::first_type& key,
                const typename Collection::value_type::second_type&& value) {
    static_assert(
        !std::is_same<Collection, Collection>::value, // always false
        "No rvalue references allowed!");
}

std::is_same是为了让static_assert依赖于模板参数,否则即使不调用重载也会导致编译错误。

编辑:这是一个最小的完整示例:

void foo(char const&) { };
void foo(char const&&) = delete;

int main()
{
    char c = 'c';
    foo(c);   // OK
    foo('x'); // Compiler error
}

MSVC 在第二次调用 foo 时出现以下错误:

rval.cpp(8) : error C2280: 'void foo(const char &&)' : attempting to reference a deleted function
        rval.cpp(2): See declaration of 'foo'

但是,第一次调用可以正常工作,如果您注释掉第二次调用,程序就会编译。

【讨论】:

  • 临时对象!= 右值引用。您可以将 const& 用于临时对象。
  • @user1095108: 是的,但是临时对象将至少与引用一样长,并且不那么容易意外传递(我假设您的意思是char const&amp; ct = 't'; foo(ct);)。
  • 谢谢!我不知道您可以对全局函数使用“删除”修饰符。
  • 比我最初想到的要干净得多,它采用通用参考和 SFINAE 过滤器,它应该是 Collection::value_type::second_type 类型的左值。
猜你喜欢
  • 1970-01-01
  • 2012-07-05
  • 2014-09-09
  • 1970-01-01
  • 2018-09-09
  • 1970-01-01
  • 2020-11-27
  • 1970-01-01
相关资源
最近更新 更多