【问题标题】:universal reference c++11 code duplication通用参考c++11代码重复
【发布时间】:2015-09-25 08:50:01
【问题描述】:

在我的项目中,我有这样的功能:

bool VectorList::put(const Pair &p);

这通过复制PairPair 添加到VectorList

我可以这样使用它:

Pair p { "key", "value" };

VectorList v;
v.put(p);

// or
v.put(Pair{ "anotherkey", "anothervalue" });

但是在第二种情况下,创建了一个不必要的对象,所以我想做

bool VectorList::put(Pair &&p);

我检查了这是如何在向量 (gcc, llvm) 中完成的,并且在这两种方法中都有 100% 相同的代码,除了相等 / std::move() 行。

有没有什么方法可以在不重复代码的情况下做到这一点?


put() 与此类似:

struct Node{
    Pair pair;
    AdditionalThings at;
};

bool VectorList::put(const Pair &p){
    if (not_good_for_insert(p))
         return false;
    // ...
    Node node = create_node();
    node.pair = p;
    // ...
    return true;
}

【问题讨论】:

    标签: c++ c++11 dry universal-reference


    【解决方案1】:

    是的,使用perfect forwarding

    template <typename P>
    bool VectorList::put (P &&p) {
        //can't forward p here as it could move p and we need it later
        if (not_good_for_insert(p)) 
         return false;
        // ...
        Node node = create_node();
        node.pair = std::forward<P>(p);
        // ...
        return true;
    }
    

    另一种可能性是像Maxim's answer 那样通过值传递。完美转发版本的优点是,如果您传入兼容的参数,它不需要中间转换,并且如果移动成本很高,则性​​能更好。缺点是转发引用函数非常贪婪,因此其他重载可能不会按照您的意愿行事。

    请注意,Pair &amp;&amp;p 不是通用引用,它只是一个右值引用。通用(或转发)引用在推导的上下文中需要一个右值,例如模板参数。

    【讨论】:

    • 你能否详细说明如果我有这样的代码会是什么样子 - struct Node{ Pair pair, SomethingElse se } xx; xx.pair = p;我也会编辑问题。
    • 如果我知道我的类型将始终为 Pair,是否需要模板?
    • 我不能只做:node.pair = std::forward(p);我的意思是,它毕竟是一个类型名?
    • @Nick 不,这不是转发引用的工作方式。请阅读this
    【解决方案2】:

    理想的解决方案是接受通用参考,正如TartanLlama 建议的那样。

    如果您能负担得起头文件中的函数定义,那么理想的解决方案就会奏效。如果您的函数定义不能在标头中公开(例如,您使用 Pimpl idiom 或基于接口的设计,或者该函数驻留在共享库中),则第二个最佳选择是按值接受。这样,调用者可以选择如何构造参数(复制、移动、统一初始化)。但是,被调用者将不得不为一招付出代价。例如。 bool VectorList::put(Pair p);:

    VectorList v;
    Pair p { "key", "value" };
    v.put(p); 
    v.put(std::move(p));
    v.put(Pair{ "anotherkey", "anothervalue" });
    v.put({ "anotherkey", "anothervalue" });
    

    在实现中,您从参数中移出:

    bool VectorList::put(Pair p) { container_.push_back(std::move(p)); }
    

    另一条评论是,您可能喜欢使用标准 C++ 名称来进行容器操作,例如 push_back/push_front,以便清楚它的作用。 put 晦涩难懂,需要您代码的读者查看源代码或文档以了解发生了什么。

    【讨论】:

      【解决方案3】:

      在 TartanLlama 的帮助下,我制作了以下测试代码:

      #include <utility>
      #include <iostream>
      #include <string>
      
      class MyClass{
      public:
          MyClass(int s2) : s(s2){
              std::cout << "c-tor " << s << std::endl;
          }
      
          MyClass(MyClass &&other) : s(other.s){
              other.s = -1;
      
              std::cout << "move c-tor " << s << std::endl;
          }
      
          MyClass(const MyClass &other) : s(other.s){
              std::cout << "copy c-tor " << s << std::endl;
          }
      
          ~MyClass(){
              std::cout << "d-tor " << s << std::endl;
          }
      
      public:
          int s;
      };
      
      // ==============================
      
      template <typename T>
      MyClass process(T &&p){
          MyClass out = std::forward<T>(p);
      
          return out;
      }
      
      // ==============================
      
      void t1(){
          MyClass out = process( 100 );
      }
      
      void t2(){
          MyClass out = process( MyClass(100) );
      }
      
      void t3(){
          MyClass in  = 100;
          MyClass out = process(std::move(in));
      
          std::cout <<  in.s << std::endl;
          std::cout << out.s << std::endl;
      }
      
      void t4(){
          MyClass in  = 100;
          MyClass out = process(in);
      
          std::cout <<  in.s << std::endl;
          std::cout << out.s << std::endl;
      }
      
      
      int main(int argc, char** argv){
          std::cout << "testing fast c-tor"   << std::endl;   t1();   std::cout << std::endl;
          std::cout << "testing c-tor"        << std::endl;   t2();   std::cout << std::endl;
          std::cout << "testing move object"  << std::endl;   t3();   std::cout << std::endl;
          std::cout << "testing normal object"    << std::endl;   t4();   std::cout << std::endl;
      }
      

      gcc 的输出如下:

      testing fast c-tor
      c-tor 100
      d-tor 100
      
      testing c-tor
      c-tor 100
      move c-tor 100
      d-tor -1
      d-tor 100
      
      testing move object
      c-tor 100
      move c-tor 100
      -1
      100
      d-tor 100
      d-tor -1
      
      testing normal object
      c-tor 100
      copy c-tor 100
      100
      100
      d-tor 100
      d-tor 100
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-12-06
        • 1970-01-01
        • 2018-10-01
        • 1970-01-01
        • 2017-09-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多