无法理解为什么完美转发不起作用

时间:2016-03-12 02:13:41

标签: c++ templates c++11 perfect-forwarding

我试图了解完美转发的工作原理,但我无法理解为什么在下面的代码中调用了复制构造函数

#include <utility>
#include <iostream>
using std::cout;
using std::endl;

class Something {
public:
    Something() = default;
    Something(__attribute__((unused)) const Something& other) {
        cout << "Copy constructor called" << endl;
    }
    Something(__attribute__((unused)) Something&& other) {
        cout << "Move constructor called" << endl;
    }

    void print() {
        cout << "Something::print() called" << endl;
    }
};

void function_1(Something&& one) {
    cout << "version two called" << endl;
    Something inner{one};
    inner.print();
}
void function_1(const Something& one) {
    Something inner(one);
    inner.print();
}

template <typename... T>
void test_function(T&&... ts) {
    function_1(std::forward<T>(ts)...);
}

int main() {

    const Something some1 {Something()};

    test_function(some1);
    test_function(Something());

    return 0;
}

这会产生以下输出

Copy constructor called
Something::print() called
version two called
Copy constructor called
Something::print() called

在rvalue参考中更改代码以包含std::move,但我没想到会需要它。当引用是右值引用时,应该自动调用正确的构造函数吗?正确的引用已解决,但正在调用错误的构造函数。任何帮助将不胜感激!

2 个答案:

答案 0 :(得分:4)

右值引用绑定到右值。它本身不是一个右值,因为它有一个名字。

但是在使用时具有名称的任何东西在默认情况下都是左值,甚至是右值引用。您的代码可以使用Something&& one三次,如果第一次使用隐式move,则会被搞砸。

相反,它是使用点的左值(默认情况下),它绑定到右值。

当您想要发出信号时,不再要求其状态持续存在,std::move它。

完美转发可用于写function_1两个std::forward<Blah>(blah),方法是将Floor::Floor(const string &f_name) { ifstream tmp(f_name.c_str()); tmp.ignore(100,'\n'); int c; while(!tmp.eof()) { c = tmp.get(); if (c != ' ' && c != '#' && c != 'L' && c != 'D' && c != 'K' && c != 'X' && c != 'E' && c != 'S' && c != 'A' && c != '\n' && c != EOF) { cout << "Bad input file" << endl; exit(EXIT_FAILURE); } } fill_floor(f_name); 放在您想要从blah移动的位置(如果它是右值参考)。

现在上面充满了谎言,因为有xvalues prvalues lvalues等 - 标准更复杂。例如,在return语句中使用变量可以将命名值转换为rvalue。但基本的经验法则是值得知道的:它有一个名称,它是一个左值(除非明确地转换或过期)。

答案 1 :(得分:1)

此代码将调用copy ctor,而不是move ctor。

void function_1(Something&& one) {
    cout << "version two called" << endl;
    Something inner{one};
    inner.print();
}

此代码调用move ctor。

void function_1(Something&& one) {
    cout << "version two called" << endl;
    Something inner{std::move(one)};
    inner.print();
}

表达式one在技术上是一个l值。它指的是右值参考。但要实际获得rvalue-reference,您必须使用std::move。通常,任何具有名称的都是l值。未命名的临时对象,例如Something()中的main()表达式:

test_function(Something());

可以是rvalue,可以在不使用std::move的情况下调用移动。