1

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

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;
}

这种参数匹配差异的原因是什么,如何使模板版本有效?

[更新]

我考虑了您的答案,并且我将示例更改为稍微相反的方式,但结果仍然相同 - 模板版本抱怨缺少运算符 +,而非模板工作正常。现在我有明确的强制转换运算符到明确的包装类:

模板版本

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;
4

2 回答 2

4

模板参数推导出现重载决议之前。但是模板参数推导不考虑隐式转换,因此您的模板化运算符甚至不会被考虑。

隐式转换仅在选择重载后应用。

不过,你可以说A a3 = W(a1) + W(a2);

于 2012-10-05T11:58:20.210 回答
2

您的第一个示例失败了,因为标准说它必须使用模板失败。与参数相关的名称查找存在潜在问题,而模板加剧了这些问题。解决此问题的一种方法是从此类查找中排除模板,除非您明确说明模板的使用。以下是 C++03 标准第 14.8.1 节第 6 段的相关文本:

对于简单的函数名称,即使函数名称在调用范围内不可见,也会应用与参数相关的查找 (3.4.2)。这是因为调用仍然具有函数调用 (3.4.1) 的语法形式。但是,当使用具有显式模板参数的函数模板时,调用不具有正确的语法形式,除非在调用点存在具有该名称的函数模板可见。如果没有这样的名称可见,则该调用在语法上不是格式正确的,并且不适用依赖于参数的查找。如果某些此类名称可见,则应用依赖于参数的查找,并且可以在其他名称空间中找到其他函数模板。

更新:
该问题已用附加信息进行了编辑。

我试图避免这种解决方案:
A a3 = W(a1) + W(a2);

为什么,确切地说,你试图避免这种情况?你的这段代码违反了编程的两个关键规则。

  1. 编写代码,就好像下一个维护您的代码的人是一个知道您住在哪里的杀人狂。
  2. 最小惊奇原则。

即使您的非模板版本也违反了这些规则。您a1+a2通过(隐藏)转换为不相关的 class进行计算Woperator+其中返回 anA而不是 a W。这不是最不惊讶的原则。至少可以说,这令人惊讶。您为什么不认为明确转换是一种更好的方法?

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

于 2012-10-05T12:05:18.410 回答