为什么下面的类模板匹配不模棱两可

Why is the following class template matching not ambiguous?

本文关键字:模棱两可 为什么      更新时间:2023-10-16
#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_func重载犹豫不决时,可以毫不费力地消除对is_rvalue_ref调用的歧义:

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]

对于两个类模板部分专用化,第一个至少是 与第二个 if 一样专业,给定以下重写为两个 函数模板,第一个函数模板至少为 根据功能的排序规则专门作为第二 模板 (14.5.6.2(:

— 第一个函数模板具有相同的 模板参数作为第一个部分专用化,并具有 类型为类模板的单个函数参数 使用第一个部分的模板参数进行专用化 专业化,以及

— 第二个函数模板具有相同的 模板参数作为第二个部分专用化,并具有 类型为类模板的单个函数参数 使用第二个部分的模板参数进行专用化 专业化。

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

如果匹配,则会自动优先于主要专业化。[temp.class.spec.match]/1:

当类模板在需要 实例化类时,有必要确定是否 实例化将使用主模板或以下模板之一生成 部分专业化。这是通过匹配模板来完成的 类模板专用化与模板的参数 部分专业化的参数列表。

(1.1( — 如果正好找到一个匹配的专业化,则 实例化是从该专用化生成的。

对于函数调用,部分排序启动 - 并且为了参数推导的目的,它忽略了引用,[temp.deduct.partial]/5:

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

(5.1( — 如果P是引用类型,则P替换为该类型 提到。

。这使得函数模板等效于部分排序。因此,该呼吁是模棱两可的。

在上面的示例中,is_rvalue_ref_func重载由下式获得 精确地执行is_rvalue_ref主数据库的重写 模板和部分专业化。

几乎不。

第一个函数模板具有与 第一个部分专用化,并且具有单个函数参数,其 类型是具有模板参数的类模板专用化 第一个部分专业化

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

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

函数模板不是部分专用的。您的报价仅适用于部分类模板专用化。它使用函数重载解析的规则来定义哪个专用化更专用。

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

如果你想使用函数模板来确定某物是否是右值,你可以做这样的事情(我认为;目前我不能轻易测试代码(:

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

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

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