Путаница с конструкторами перемещения: невозможно вызвать конструктор перемещения

У меня возникли трудности с пониманием конструкторов перемещения в C++. Я сделал простой класс с конструктором по умолчанию, конструктором копирования, конструктором перемещения и деструктором. Кроме того, я определил функцию с двумя перегрузками: одна принимает ссылку на этот класс, а другая принимает ссылку rvalue на этот класс. Мой тестовый код ниже.

#include <iostream>


class c {

public:

    c() {
        std::cout << "default constructor" << std::endl;
    }

    c(const c& s) {
        std::cout << "copy constructor" << std::endl;
    }

    c(c&& s) {
        std::cout << "move constructor" << std::endl;
    }

    ~c() {
        std::cout << "destructor" << std::endl;
    }

};

void f(c& s) {
    std::cout << "passed by reference" << std::endl;
}

void f(c&& s) {
    std::cout << "passed by rvalue reference" << std::endl;
}

int main() {

    c s1; // line 1
    std::cout << "\n";
    c s2(s1); // line 2
    std::cout << "\n";
    c s3(c()); // line 3

    std::cout << "\n";

    f(s1); // line 4
    std::cout << "\n";
    f(c()); // line 5

    getchar();
    return 0;

}

Результат, который я получаю, не такой, как я ожидал. Ниже приведен вывод, который я получаю из этого кода.

default constructor

copy constructor

passed by reference

default constructor
passed by rvalue reference
destructor

Я могу понять вывод всех строк, кроме line 3. На line 3, который равен c s3(c());, c() является значением r, поэтому я ожидаю, что s3 будет построено перемещением. Но вывод не показывает, что он создан для перемещения. На line 5 я делаю то же самое и передаю rvalue функции f(), и она действительно вызывает перегрузку, которая принимает ссылку rvalue. Я очень смущен и буду признателен за любую информацию по этому поводу.

Изменить: я могу вызвать конструктор перемещения, если выполню c s3(std::move(c()));, но не передаю ли уже rvalue в s3? Зачем мне std::move?


person Deniz    schedule 02.06.2017    source источник
comment
@NathanOliver На самом деле это не дубликат этого. То, что здесь происходит, - это самый неприятный анализ, а не копирование.   -  person Angew is no longer proud of SO    schedule 02.06.2017
comment
@Angew Хороший звонок. Пропустил это.   -  person NathanOliver    schedule 02.06.2017
comment
@Angew На самом деле я пытался создать объект с помощью его конструктора перемещения, и когда НатанОливер направил меня на другой вопрос, я действительно подумал, что это дубликат. Я даже не заметил, что оператор в строке 3 был сигнатурой функции, пока вы не указали. Я также никогда не слышал о самом неприятном синтаксическом анализе, так что спасибо, что поделились им со мной; Я буду читать об этом, чтобы узнать, что это такое.   -  person Deniz    schedule 02.06.2017


Ответы (2)


Причина, по которой вы не видите никакого вывода из строки 3, заключается в том, что она объявляет функцию, а не переменную. Это связано с двусмысленностью, называемой Самый раздражающий анализ.

Сравните c s3(c()) с int foo(int ()), который, благодаря неявным корректировкам типов, совпадает с int foo(int (*f)()).

Чтобы обойти это, используйте инициализацию скобок (которая была введена в С++ 11 отчасти по этой причине):

c s3(c{});

// or

c s3{c()};

// or

c s3{c{}};
person Angew is no longer proud of SO    schedule 02.06.2017
comment
Спасибо за ваш информативный ответ. И этот, и ответ М.М. ниже отвечают на мой вопрос. Однако, видимо, вы были на минуту раньше, чем они, поэтому я приму ваш ответ. Спасибо и М.М. за ваше время и помощь. - person Deniz; 02.06.2017

c s3(c()); не создает никаких объектов. Это объявление функции. Функция называется s3, тип возвращаемого значения — c, а тип параметра — «указатель на функцию, не принимающую параметров и возвращающую c». Таким образом, вы не получаете никакого вывода, так как объявление функции не вызывает никаких функций.

Чтобы избежать такого рода вещей, вы можете использовать инициализацию списка, c s3{c()};, вероятно, больше соответствует тому, что вы имели в виду.

person M.M    schedule 02.06.2017
comment
+1 за информацию «тип параметра — это указатель на функцию, не принимающую параметров и возвращающую c». если бы это было c(x), то тип параметра был бы c, а имя было бы x - person Yusuf R. Karagöz; 02.06.2017