【问题标题】:C++ Passing function objects as lvalues and/or rvaluesC++ 将函数对象作为左值和/或右值传递
【发布时间】:2018-12-13 17:20:01
【问题描述】:

我有一个类应该根据用户提供的谓词过滤其内容。给我的界面规定了对谓词的引用:

class Test {
  vector<int> data;
public:
  template <class PREDTYPE>
  void filter(PREDTYPE& pred) {
    return;
  }
};

我还给了一段测试代码,大致是这样的:

class Filter {
public:
  bool operator()(int) const {
    return false;
  }
};

int main() {
  Test test;
  test.filter(Filter());
}

这不编译,说cannot bind non-const lvalue reference of type 'Filter&amp;' to an rvalue of type 'Filter'。如果我将测试代码更改为

int main() {
  Test test;
  Filter filter;
  test.filter(filter);
}

它有效,但这取决于最终用户,我无法控制他们的行为。我尝试重载过滤器方法,创建一个可以按值接受谓词然后通过引用传递它的版本,但这也无法编译,并显示消息call of overloaded 'filter(Filter&amp;)' is ambiguous。

因此我的问题是:是否可以构造一个同时接受谓词的右值和左值的过滤器?

【问题讨论】:

    标签: c++ templates function-object


    【解决方案1】:

    简而言之,是的,它是 (C++11)

    您只需要依靠参考折叠规则来确保这一点:

    template <typename PREDTYPE>
    void filter(PREDTYPE&& pred) { // notice the &&
        // ... Whatever
        // Use perfect forwarding *IF* you can
        std::forward<PREDTYPE>(pred)(some_stuff);
        // Do not use pred after it has been forwarded!
    }
    

    这将同时接受右值和左值,而不必依赖const- 引用(因此您的谓词仍然可以是可变的)。如果您坚持使用较旧的 C++ 标准,最好的办法是改用 const 引用(上面突出显示的警告)或将过滤器嵌入std::function 并依赖调用站点的隐式转换。

    【讨论】:

      【解决方案2】:

      这取决于函数对谓词的作用。如果它在返回后保留指向它的引用/指针,则您必须提供能够存活足够长的东西。但是,如果它没有保留传入的谓词对象的任何内存,则可以将谓词强制转换为左值:

      test.filter(static_cast<Filter&>(Filter()));
      

      或者将它封装在一个简短的实用函数中:

      template <class T>
      T& stay(T&& a)
      { return a; }
      
      test.filter(stay(Filter()));
      

      【讨论】:

        【解决方案3】:

        您可能希望将您的成员函数签名更改为

        template <class PREDTYPE> void filter(PREDTYPE&& pred)
        

        在这里,谓词以forwarding reference 的形式传递,它折叠为传入的左值引用的左值引用,以及传入的右值引用的右值引用。

        【讨论】:

        • 这修复了左值/右值的问题,但类接口发生了变化。这是不可避免的吗?
        • 至少我想不出将右值谓词传递给只接受非常量左值引用的函数的解决方案...
        • 谓词通常是无状态的,因此按值传递它们是“免费的”(请参阅​​下面的答案)。通用引用总是一个引用,所以除非你依赖内联,否则你传递一个指针。
        【解决方案4】:

        通常,函数对象应该按值传递。所以不要超载并将您的签名更改为

        template <class PREDTYPE> void filter(PREDTYPE pred)
        

        可以在here 找到为什么这样更好的解释。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多