为什么参数匹配适用于非模板版本,但不适用于模板版本?

时间:2012-10-05 11:44:04

标签: c++ templates operator-overloading

考虑这个非常简单的示例,其中我有模板包装类,其中定义了运算符:

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;

2 个答案:

答案 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);

为什么,你究竟想避免这种情况?您的此代码违反了两个关键的编程规则。

  1. 代码好像下一个维护你的代码的人是一个杀人的疯子,知道你住在哪里。
  2. 最不惊讶的原则。
  3. 即使您的非模板版本也违反了这些规则。您通过(隐藏)转换为无关的类a1+a2来计算Woperator+返回A而不是W。这不是最不惊讶的原则。至少可以说这是令人惊讶的。 为什么不认为明确转换是一种更好的方法?

    依赖于参数的查找是一个非常强大的工具,但是存在许多与此功能相关的问题。将ADL与自动类型转换相结合并将问题放大。将它与自动类型转换和模板结合起来,你就搞得一团糟。标准委员会决定足够了。除非您明确转换,否则您无法执行您想要执行的操作。