【问题标题】:Why argument matching works for non template version, but does not work for template version?为什么参数匹配适用于非模板版本,但不适用于模板版本?
【发布时间】:2012-10-05 11:44:03
【问题描述】:

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

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;

【问题讨论】:

    标签: c++ templates operator-overloading


    【解决方案1】:

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

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

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

    【讨论】:

    • 所以编译器是正确的,但是这是我想要避免的。也许还有另一种方法可以通过模板包装器实现A a3 = a1 + a2
    • 我已经更新了我的问题。你的答案还适用吗?
    • @PiotrNycz:不;你仍然依赖隐式转换。为什么不提供带有 is_constructible_from 的 SFINAE 模板呢?
    • 你的意思是template &lt;class T, class W&gt; T operator + (W,W)
    • @PiotrNycz:不,我有更多的事情要考虑,实际上是依赖std::is_constructible...
    【解决方案2】:

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

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

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

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

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

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

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

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

    【讨论】:

    • “在调用点可见具有该名称的函数模板”是什么意思
    • 我之前没有注意到你的更新。我同意,但是……这不是真正的代码。我只是用它作为例子来只显示问题而不是所有“环境”。考虑到这里的发帖人总是被要求发布仅显示问题的代码,但随后他们被批评为愚蠢的编程方式......我会记得添加注意代码只是人工插图。
    猜你喜欢
    • 1970-01-01
    • 2012-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多