Ссылочный конструктор Lvalue вызывается вместо ссылочного конструктора rvalue

c++ c++11 move-semantics rvalue-reference

756 просмотра

1 ответ

Вот этот код:

#include <iostream>

class F {
public:
   F() = default;
   F(F&&) {
      std::cout << "F(F&&)" << std::endl;
   }
   F(F&) {
      std::cout << "F(F&)" << std::endl;
   }
};

class G {
   F f_;
public:
   G(F&& f) : f_(f) {
      std::cout << "G()" << std::endl;
   }
};

int main(){
   G g = F();
   return 0;
}

Выход:

F(F&)
G()

Почему F(F&)конструктор вызывается вместо F(F&&)конструктора в конструкторе класса G? Параметр для конструктора класса G- F&& fэто ссылка на значение, но вызывается конструктор для ссылки на значение.

Автор: scdmb Источник Размещён: 12.11.2019 09:42

Ответы (1)


9 плюса

Решение

Почему конструктор F (F &) вызывается вместо конструктора F (F &&) в конструкторе класса G?

Потому fчто это lvalue. Несмотря на то, что он связан с rvalue и его тип является ссылкой на rvalue F, он также является именованной переменной . Это делает это lvalue. Не забывайте, что категория значений объекта не определяется его типом , и наоборот.

Когда вы передаете lvalue в функцию, к нему могут быть привязаны только ссылки lvalue. Вы должны изменить свой код следующим образом, если вы хотите перехватывать только значения:

class G {
    F f_;
public:
    G(F&& f) : f_(std::move(f)) {
       std::cout << "G()" << std::endl;
    }
};

В качестве альтернативы вы можете использовать std::forward<>(), что эквивалентно в этом случае, но делает ваше намерение пересылки f еще более ясным:

class G {
    F f_;
public:
    G(F&& f) : f_(std::forward<F>(f)) {
       std::cout << "G()" << std::endl;
    }
};

Теперь это последнее определение легко расширить, чтобы Fк параметру могли быть привязаны как lvalues, так и rvalues ​​типа f:

class G {
    F f_;
public:
    template<typename F>
    G(F&& f) : f_(std::forward<F>(f)) {
       std::cout << "G()" << std::endl;
    }
};

Это позволяет, например, построить экземпляр Gследующим образом:

F f;
G g(f); // Would not be possible with a constructor accepting only rvalues

В этой последней версии есть предостережение : ваш конструктор также будет работать как конструктор копирования , так что вы можете явно определить все возможные конструкторы копирования, чтобы избежать неловких ситуаций:

class G {
    F f_;
public:
    template<typename F>
    G(F&& f) : f_(std::forward<F>(f)) {
       std::cout << "G()" << std::endl;
    }
    G(G const&) = default;
    G(G&); // Must be defaulted out-of-class because of the reference to non-const
};

G::G(G&) = default;

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

Автор: Andy Prowl Размещён: 09.03.2013 07:10
Вопросы из категории :
32x32