模板中的模板函数重载 class

Overloading of template function in template class

我在模板化 class 中有一个模板化运算符,我想更改其针对特定类型的行为。我的代码:

#include <iostream>

template <typename N>
struct A {
  int x;
  template<typename T>
  A& operator<<(const T& t) {
    x += static_cast<int>(t);
    return *this;
  }
};


enum class B {
  s,t
};

template <typename N>
A<N>& operator<<(A<N>& a, const B& b) {
  a.x -= static_cast<int>(b);
  return a;
}

int main() {
  A<int> a{3};
  std::cout << (a<<1).x << " " << (a << B::s).x;
}

g++-4.9 编译正常,但 clang-3.6 抱怨它不明确。

注意,如果 class 没有模板化,那么两个编译器都可以编译它。

什么是正确的行为?

简短摘要:我相信这是模板部分排序规则中的 gcc 错误,而且 clang 是正确的。我提交了 bug 66914, although it's probably a duplicate of bug 53499 直到后来我才注意到。


通话中

a << B::s;

我们有两个可行的候选人:

template <typename T> A<int>::operator<<(const T& );
template <typename N> operator<<(A<N>&, const B& );

您可以重写成员函数以将对实例的引用作为第一个参数,并将两个都写为实例。所以我们有:

template <> operator<<(A<int>&, const B& ); // [T = B]
template <> operator<<(A<int>&, const B& ); // [N = int]

由于两者都是可行的候选者,让我们通过 [over.match.best] 中的规则来确定哪一个是最佳可行的候选者:

Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then

— for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or, if not that,

不,两者采用完全相同的参数,因此转换序列相同。

— the context is an initialization by user-defined conversion [ ... ]

不,无关紧要。

— the context is an initialization by conversion function [ ... ]

不,无关紧要。

— F1 is not a function template specialization and F2 is a function template specialization, or, if not that,

不,两者 都是函数模板特化。

— F1 and F2 are function template specializations, and the function template for F1 is more specialized than the template for F2 according to the partial ordering rules described in 14.5.6.2.

这是最复杂的规则。最终,两者都不比对方更专业。为什么?成员函数实际上是:

template <typename T> A<int>& operator<<(A<int>&, const T& );

如果我们为 T 合成一个类型(称其为 Unique1),推导将对自由函数失败(因为 Unique1 不匹配 B) .另一方面,如果我们为 N 合成一个类型(称之为 Unique2),推导将对成员函数失败(因为 Unique2 不匹配 int) .

由于两个函数都不比另一个更专业,我们 运行 没有要点。函数调用应该是不明确的,这是一个 gcc 错误。

这个:

template <typename N>
A<N>& operator<<(A<N>& a, const B& b) {
  a.x -= static_cast<int>(b);
  return a;
}

不是你的成员运算符的重载(我相信从标准的角度来看有更正确的说法),也不是特化,它是一个参与重载的全局模板运算符分辨率。

从这个角度来看,两者同样完美匹配,所以我认为 clang 是正确的。