2

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

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

这通过复制Pair将.VectorListPair

我可以这样使用它:

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

VectorList v;
v.put(p);

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

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

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

我检查了这是如何在向量(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;
}
4

3 回答 3

7

是的,使用完美转发

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 &&p不是通用引用,它只是一个右值引用。通用(或转发)引用在推导的上下文中需要一个右值,例如模板参数。

于 2015-09-25T08:54:05.457 回答
2

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

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

于 2015-09-25T08:55:56.777 回答
0

在 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
于 2015-09-25T12:03:09.813 回答