通用参考c++11代码重复

universal reference c++11 code duplication

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

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

我检查了这是如何在 vector (gcc, llvm) 中完成的,两种方法中的代码 100% 相同,除了 equal / 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;
}

是,使用 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;
}

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

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

理想的解决方案是按照 TartanLlama 的建议接受通用引用。

如果您负担得起 header 文件中的函数定义,那么理想的解决方案是有效的。如果您的函数定义不能在 header 中公开(例如,您使用 Pimpl 惯用语或 interface-based 设计,或者函数驻留在共享库中),第二个最佳选择是按值接受。这样调用者可以选择如何构造参数(复制、移动、统一初始化)。不过,被叫方将不得不付出一步的代价。例如。 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 晦涩难懂,需要代码的读者查看源代码或文档以了解发生了什么。

在 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