Template deduction/overload resolution favors T&& over const T&

為{幸葍}努か 提交于 2019-12-24 16:54:57

问题


I have a pair of function templates defined like so:

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
}

If I call f as below:

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

The VC++ compiler favors the && overload, apparently because it is less specialized. I would like the const& overload to be called in this case--the && version is intended for constructions like f(ReturnAVector()). Is there a way to achieve this without manually specifying the template argument?

After a fair amount of effort, I came up with this:

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
}

But wow; is that really the simplest way to get what I'm after?


回答1:


With:

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

you call f(std::vector<int>&) so

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

is an exact match (universal reference) CollectionType is std::vector<int>& whereas

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

requires a const promotion.

A possible solution is to add a version non const:

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

or to forward your argument, something like:

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



回答2:


The second overload is always an exact match. So there is in fact no need for the first overload and it can only cause ambiguity. Instead of moving, you should forward the argument.

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

Scott Meyers has given an excellent explanation of this: http://scottmeyers.blogspot.nl/2012/11/universal-references-in-c11-now-online.html



来源:https://stackoverflow.com/questions/25938749/template-deduction-overload-resolution-favors-t-over-const-t

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!