2015-06-06 3 views
3

Недавно я начал использовать C++. И у меня есть вопрос о std :: forward с ссылкой lvalue и ссылкой rvalue. В моем понимании, Func (mc) в следующем коде предполагается вызвать Func (T & t) из-за правила вычитания шаблона. И конструктор копирования MyClass должен быть вызван внутри функции с сообщением «конструктор копирования». Однако я не могу получить его, когда запускаю программу. Я проверил, что std :: forwad с ссылкой rvalue и конструктором копий хорошо работает в других строках. Пожалуйста, помогите мне понять, что происходит в коде. Если я сделаю легкую ошибку или недоразумение, я сожалею о том, что уделил вам время. Большое спасибо.std :: конструктор форварда и копирования

class MyClass { 
public: 
    MyClass() { printf("constructor.\n"); }; 
    MyClass(MyClass&) { printf("copy constructor.\n"); }; 
    MyClass(const MyClass&) { printf("const copy constructor.\n"); }; 
    MyClass(MyClass&&) { printf("move constructor.\n"); }; 
    int val = 3; 
}; 

template <typename T> 
void Func(T&& t) { 
    T new_t_(std::forward<T>(t)); 
    new_t_.val *= 2; 
}; 

main() { 
    MyClass mc; 
    Func(mc); // lvalue <- Func(T&) 
    Func(MyClass()); // rsvalue <- Func(T&&) 
    printf("mc.val=%d\n", mc.val); // this is for check 

    MyClass mc2(mc); // this is for check of copy constructor 
} 

Вывода при запуске программы следующего,

constructor. 
constructor. 
move constructor. 
mc.val=6 
copy constructor. 

Я думаю, что он должен иметь «конструктор копирования» между первым и вторыми «конструкторами» сообщениями.

Еще раз спасибо.

+0

Конструктор 'MyClass (MyClass &)' не очень полезен для многих классов.С шаблоном RAII обычно следует только [Правило пятерки] (http://www.cppsamples.com/common-tasks/rule-of-five.html). – dyp

+0

Спасибо, dyp. Я положил MyClass (MyClass &), чтобы проверить, что этот конструктор копирования не вызывается вместо MyClass (const MyClass &), хотя теперь я знаю, что оба они не вызываются в Func(). – mora

ответ

5
Func(mc); 

Этот вызов будет выводить T быть MyClass&. Обратите внимание на ссылку. Это связано с тем, что в C++ введена совершенная переадресация: A ссылка на перенаправление, такая как T&&, параметр функции Func будет считаться ссылкой на lvalue, если соответствующий аргумент функции является lvalue-expression; в противном случае (для значений x и prvalues) это не будет выводиться как ссылочный тип.

Тип, выведенный для T, будет затем сбрасываться с помощью && из параметра функции T&& t, чтобы сформировать окончательный тип параметра функции. В случае Func(mc), T == MyClass&, поэтому параметр функции становится MyClass& &&, свернутый до MyClass&.

С T == MyClass&, объявление локальной переменной new_t_ будет объявить ссылку в этом случае. Ни один новый объект не будет создан:

template <> 
void Func<MyClass&>(MyClass& t) { 
    MyClass& new_t_(std::forward<MyClass&>(t)); 
    new_t_.val *= 2; 
} 

Func(MyClass()); 

Здесь функция аргумента является prvalue выражение MyClass(). T будет выведено на MyClass (без ссылки). Шаблон функции конкретизации производства выглядит следующим образом:

template <> 
void Func<MyClass>(MyClass&& t) { 
    MyClass new_t_(std::forward<MyClass>(t)); 
    new_t_.val *= 2; 
} 

Если вы хотите скопировать/переместить аргумент функции в локальной переменной, std::decay metafunction можно использовать - или, более конкретно к проблеме операционного, в std::remove_reference metafunction. Например.

template <typename T> 
void Func(T&& t) { 
    using DT = typename std::decay<T>::type; 
    DT new_t_(std::forward<T>(t)); 
    new_t_.val *= 2; 
} 
+0

Большое спасибо, dyp. Это был очень хороший ответ. И, пожалуйста, простите меня, чтобы использовать комментарии, чтобы выразить свою благодарность вам. Я начинаю переполнение стека, и я не знал, как вам ответить. – mora

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