2014-09-19 3 views
1

У меня есть пара шаблонов функций, определенная следующим образом:Шаблон дедукции разрешение/перегрузка способствует T && над сопзЬ T &

template<typename CollectionType> 
Foo<CollectionType> f(const CollectionType& v) 
{ 
    return Foo<CollectionType>(v); // copies v into a member variable 
} 

template<typename CollectionType> 
Foo<CollectionType> f(CollectionType&& v) 
{ 
    return Foo<CollectionType>(std::move(v)); // moves v into a member variable 
} 

Если я называю f как показано ниже:

std::vector<int> v; 
f(v); 

В VC++ компилятор благоволит && перегрузка, видимо, потому что это less specialized. Я бы хотел, чтобы в этом случае была вызвана перегрузка const& - версия && предназначена для таких конструкций, как f(ReturnAVector()). Есть ли способ достичь этого без указания аргумента шаблона вручную?

После изрядного количества усилий, я пришел с этим:

template<typename CollectionType> 
Foo<CollectionType> f(const CollectionType& v) 
{ 
    return Foo<CollectionType>(v); // copies v into a member variable 
} 

template<typename CollectionType> 
typename std::enable_if<std::is_rvalue_reference<CollectionType&&>::value, 
    Foo<typename std::remove_reference<CollectionType>::type>>::type 
f(CollectionType&& v) 
{ 
    return Foo<CollectionType>(std::move(v)); // moves v into a member variable 
} 

Но ничего себе; это действительно самый простой способ получить то, что мне нужно?

ответ

3

С:

std::vector<int> v; 
f(v); 

вы звоните f(std::vector<int>&) так

template<typename CollectionType> 
Foo<CollectionType> f(CollectionType&& v) 

точное совпадение (универсальный эталон) CollectionType является std::vector<int>& тогда

template<typename CollectionType> 
Foo<CollectionType> f(const CollectionType& v) 

требует константного продвижения.

Возможное решение заключается в добавлении версии нон ПОСТОЯННЫЕ:

template<typename CollectionType> 
Foo<CollectionType> f(CollectionType& v) 

или направить свой аргумент, что-то вроде:

template<typename CollectionType> 
Foo<typename std::remove_reference<CollectionType>::type> 
f(CollectionType&& v) 
{ 
    return Foo<typename std::remove_reference<CollectionType>::type>(std::forward<CollectionType>(v)); 
} 
+0

Мне нравится идея переадресации, и она работает, как ожидалось. – dlf

3

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

template<typename CollectionType> 
Foo<CollectionType> f(CollectionType&& v) 
{ 
    return Foo<CollectionType>(std::forward<CollectionType>(v)); 
} 

Скотт Мейерс дал отличное объяснение этого: http://scottmeyers.blogspot.nl/2012/11/universal-references-in-c11-now-online.html

+0

Это была отличная статья. Ниже приведена [прямая ссылка] (http://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers). – dlf

+0

Обратите внимание: если 'CollectionType' является ссылкой lvalue (' T & '), вы возвращаете' Foo ', а не' Foo ' – Jarod42

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