考虑这个非常简单的示例,其中我有模板包装类,其中定义了运算符:
template <class T>
struct W {
W(const T&) {}
};
template <class T>
T operator + (const W<T>& w1, const W<T>& w2) { return T(); }
这令人惊讶地无法与包装类型匹配,gcc4.5.1中的简单错误无法找到operator + (A,A)
:
struct A {};
int main() {
A a1, a2;
A a3 = a1 + a2;
}
我试图通过测试非模板包装器找到原因,但这个版本有效:
struct A {};
struct WA {
WA(const A&) {}
};
A operator + (const WA& w1, const WA& w2) { return A(); }
int main() {
A a1, a2;
A a3 = a1 + a2;
}
参数匹配差异的原因是什么,以及如何使模板版本有效?
[UPDATE]
我考虑了你的答案,我把这个例子改成了一个相反的方式,仍然有相同的结果 - 模板版本抱怨缺少operator +,而非模板工作正常。现在我有显式的转换操作符显式包装类:
模板版
template <class T>
struct W {
};
template <class T>
T operator + (const W<T>& w1, const W<T>& w2) { return T(); }
struct A {
operator W<A>() const { return W<A>(); }
};
不是模板
struct WA {
};
struct A {
operator WA() const { return WA(); }
};
A operator + (const WA& w1, const WA& w2) { return A(); }
我试图避免这个解决方案:
A a3 = W(a1) + W(a2);
没有希望这个工作吗?
A a3 = a1 + a2;
答案 0 :(得分:4)
模板参数推导在重载决策之前。但模板参数推导不考虑隐式转换,因此您的模板化运算符甚至从未被考虑过。
仅在选择了重载后才应用隐式转换。
但你可以说A a3 = W(a1) + W(a2);
。
答案 1 :(得分:2)
你的第一个例子失败了,因为标准说它必须用模板失败。参数依赖名称查找存在潜在问题,模板会加剧这些问题。解决此问题的一种方法是从模板中排除此类查找,除非您明确使用模板。以下是C ++ 03标准的相关文本,第14.8.1节第6段:
对于简单函数名称,即使函数名称在调用范围内不可见,依赖于参数的查找(3.4.2)也适用。这是因为调用仍然具有函数调用的语法形式(3.4.1)。但是当使用带有显式模板参数的函数模板时,除非在调用点处有一个具有该名称的函数模板,否则调用没有正确的语法形式。如果看不到这样的名称,则调用语法不完善,并且参数依赖查找不适用。如果某些此类名称可见,则应用依赖于参数的查找,并且可以在其他名称空间中找到其他函数模板。
<强>更新强>
该问题已经过编辑,附加了其他信息。
我试图避免这种解决方案:
A a3 = W(a1) + W(a2);
为什么,你究竟想避免这种情况?您的此代码违反了两个关键的编程规则。
即使您的非模板版本也违反了这些规则。您通过(隐藏)转换为无关的类a1+a2
来计算W
,operator+
返回A
而不是W
。这不是最不惊讶的原则。至少可以说这是令人惊讶的。 为什么不认为明确转换是一种更好的方法?
依赖于参数的查找是一个非常强大的工具,但是存在许多与此功能相关的问题。将ADL与自动类型转换相结合并将问题放大。将它与自动类型转换和模板结合起来,你就搞得一团糟。标准委员会决定足够了。除非您明确转换,否则您无法执行您想要执行的操作。