Ссылочный конструктор Lvalue вызывается вместо ссылочного конструктора rvalue
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
это ссылка на значение, но вызывается конструктор для ссылки на значение.
Ответы (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
объекта. То же самое относится, конечно, к конструктору перемещения . Это оставлено как упражнение.
Вопросы из категории :
- c++ What are the barriers to understanding pointers and what can be done to overcome them?
- c++ Какой самый простой способ для анализа файла INI в C ++?
- c++ Когда вы должны использовать «друг» в C ++?
- c++ Как вы очищаете переменную stringstream?
- c++ В C ++ конструктор и деструктор могут быть встроенными функциями?
- c++11 Можно ли напечатать тип переменной в стандартном C ++?
- c++11 Проверьте, имеет ли класс функцию-член с заданной сигнатурой
- c++11 Что такое умный указатель и когда я должен его использовать?
- c++11 Как вы можете перебирать элементы std :: tuple?
- c++11 Что делает static_assert и для чего вы его используете?
- move-semantics Что такое семантика перемещения?
- move-semantics push_back против emplace_back
- move-semantics C ++ 11 значения и путаница в семантике перемещения (оператор return)
- move-semantics Переместить семантику - что это такое?
- move-semantics Как применить семантику перемещения при росте вектора?
- rvalue-reference Advantages of using forward
- rvalue-reference Правило Три становится Правилом Пяти с C ++ 11?
- rvalue-reference Есть ли какая-то польза от ссылок на const?