为什么以下类模板匹配不明确?

时间:2015-02-14 20:00:00

标签: c++ templates c++11

#include <iostream>
#include <utility>

template <class T> struct is_rvalue_ref      : std::false_type {};
template <class T> struct is_rvalue_ref<T&&> : std::true_type {};

template <typename T> bool is_rvalue_ref_func(T){return false;}
template <typename T> bool is_rvalue_ref_func(T&&){return true;}

class A {};

int main() 
{
  std::cout << std::boolalpha;
  std::cout << is_rvalue_ref<A>::value << '\n';
  std::cout << is_rvalue_ref<A&>::value << '\n';
  std::cout << is_rvalue_ref<A&&>::value << '\n';

  /*****THIS FAILS TO COMPILE************
  A a;
  A& alv = a;
  A&& arv = std::move(a);
  std::cout << is_rvalue_ref_func(a) << '\n';
  std::cout << is_rvalue_ref_func(alv) << '\n';
  std::cout << is_rvalue_ref_func(arv) << '\n';
  **************************************/   

  return 0;
}

编译器(clang 3.5 -std=c++11)在is_rvalue_ref重载is_rvalue_ref_func时对rv.cpp:31:16: error: call to 'is_rvalue_ref_func' is ambiguous std::cout << is_rvalue_ref_func(a) << '\n'; ^~~~~~~~~~~~~~~~~~ rv.cpp:8:6: note: candidate function [with T = A] bool is_rvalue_ref_func(T) ^ rv.cpp:14:6: note: candidate function [with T = A &] bool is_rvalue_ref_func(T&&) ^ rv.cpp:32:16: error: call to 'is_rvalue_ref_func' is ambiguous std::cout << is_rvalue_ref_func(alv) << '\n'; ^~~~~~~~~~~~~~~~~~ rv.cpp:8:6: note: candidate function [with T = A] bool is_rvalue_ref_func(T) ^ rv.cpp:14:6: note: candidate function [with T = A &] bool is_rvalue_ref_func(T&&) ^ rv.cpp:33:16: error: call to 'is_rvalue_ref_func' is ambiguous std::cout << is_rvalue_ref_func(arv) << '\n'; ^~~~~~~~~~~~~~~~~~ rv.cpp:8:6: note: candidate function [with T = A] bool is_rvalue_ref_func(T) ^ rv.cpp:14:6: note: candidate function [with T = A &] bool is_rvalue_ref_func(T&&) ^ 3 errors generated. 的调用消除歧义没有问题。

14.5.5.2 [temp.class.order]

然而,根据is_rvalue_ref_func

  

对于两个类模板部分特化,第一个是至少   如第二个if那样专门,给出以下重写为2   功能模板,第一个功能模板至少为   根据功能的排序规则专门作为第二个   模板(14.5.6.2):

     

- 第一个功能模板具有相同的功能   模板参数作为第一个局部特化并具有   单个函数参数,其类型是类模板   使用第一个部分的模板参数进行特化   专业化,

     

- 第二个功能模板具有相同的功能   模板参数作为第二部分特化并具有   单个函数参数,其类型是类模板   使用第二个部分的模板参数进行专门化   专业化。

在上面的示例中,is_rvalue_ref重载是通过精确执行{{1}}主模板和部分特化的重写来获得的。为什么函数调用不明确但类模板匹配定义良好?

3 个答案:

答案 0 :(得分:3)

如果匹配,则部分特化将自动优先于主要特化。 [temp.class.spec.match] / 1:

  

在需要的上下文中使用类模板时   实例化该类,有必要确定是否   实例化将使用主模板或其中一个生成   部分专业化。这是通过匹配模板来完成的   使用模板的类模板特化的参数   部分专业化的参数列表。

     

(1.1) - 如果找到一个匹配的特化,那么   实例化是从该专业化生成的。

对于函数调用,虽然部分排序开始 - 并且为了参数推断而忽略了引用,[temp.deduct.partial] / 5:

  

在部分排序完成之前,某些转换是   对用于部分排序的类型执行:

     

(5.1) - 如果P是引用类型,则P将替换为类型   提到。

...这使得函数模板等效于部分排序。因此呼叫含糊不清。

答案 1 :(得分:1)

  

在上面的例子中,is_rvalue_ref_func重载是通过获得的   正确执行is_rvalue_ref主要的重写   模板和部分专业化。

几乎没有。

  

第一个函数模板具有与之相同的模板参数   第一部分特化并具有单个函数参数   type是具有模板参数的类模板特化   第一个部分专业化

给定类模板template<class> class SomeTemplate;,正确的重写是

template<class T> void is_rvalue_ref_func(SomeTemplate<T>);
template<class T> void is_rvalue_ref_func(SomeTemplate<T&&>);

答案 2 :(得分:0)

功能模板不是部分专用的。您的引用仅适用于部分类模板专业化。它使用函数重载决策的规则来定义哪种特化更专业化。

相反,函数模板被重载,并且有两个同样好的重载。注意,你不能部分专门化功能模板,你只能完全专门化它们。

如果你想使用一个函数模板来确定某个东西是否是一个rvalue你可以做这样的事情(我想;目前我不能轻易地测试代码):

template <typename T>
constexpr bool is_rvalue_func(T&&) {
    return !std::is_reference<T>::value;
}

我认为你不能区分传递给函数的rvalues和rvalue引用。也就是说,我认为这两个都会产生true,尽管只有后者实际上是对象的右值引用:

is_rvalue_func(int());
int i(17);
is_rvalue_func(std:move(i));