2013-03-09 8 views
5

Существует этот код:Lvalue ссылка конструктор вызывается вместо RValue эталонного конструктора

#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, который является ссылкой rvalue, но вызывается конструктор для задания значения lvalue.

ответ

9

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

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

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

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; 
    } 
}; 

Теперь это последнее определение легко распространяется, так что оба lvalues ​​и rvalues ​​типа F могут быть связаны с параметром 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 

Эта последняя версия имеет нюанс хотя: ваш конструктор will basically work as a copy-constructor as well, так что вы можете явно определить все возможные конструкторы копирования, чтобы избежать неловких ситуаций:

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; 

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

+0

@ AndyProwl: Я говорю это: http://stacked-crooked.com/view?id=7f047abd729b8347039874dc9a5e8b7f18aa934a8d82d638dde2147aa94cac94. Прочитайте комментарии. – Nawaz

+0

@ Наваз: О, да, теперь я вижу вашу точку. Вы правы, я должен был объявить как const, так и неконвертную версию конструктора эксплицитных копий, иначе шаблон конструктора будет выбран в любом случае. Отредактировано, спасибо. –

+0

+1. Теперь это точно! – Nawaz

Смежные вопросы