模板中的模板函数重载 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 是正确的。
我在模板化 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 是正确的。