Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么参数匹配适用于非模板版本,但不适用于模板版本?_C++_Templates_Operator Overloading - Fatal编程技术网

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

C++ 为什么参数匹配适用于非模板版本,但不适用于模板版本?,c++,templates,operator-overloading,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(); } 我尝试通过测试非模板包装器来找到原因,但此版本可以工作: struct A {}; str

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

template <class T>
struct W {
   W(const T&) {}
};
template <class T> 
T operator + (const W<T>& w1, const W<T>& w2) { return T(); }
我尝试通过测试非模板包装器来找到原因,但此版本可以工作:

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;
}
参数匹配差异的原因是什么?如何使模板版本工作

[更新]

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

模板版本

我试图避免这种解决方案:

A a3 = W(a1) + W(a2);
这一切都没有希望了

A a3 = a1 + a2;

模板参数推导在重载解析之前。但是模板参数推导不考虑隐式转换,所以您甚至从未考虑模板化运算符。

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


你可以说a3=Wa1+Wa2;,尽管如此。

模板参数推导在重载解析之前。但是模板参数推导不考虑隐式转换,所以您甚至从未考虑模板化运算符。

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


你可以说a3=Wa1+Wa2;,但是。

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

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

更新: 该问题已用其他信息编辑

我试图避免这种解决方案: a3=Wa1+Wa2

你到底为什么要避免这种情况?你的这段代码违反了编程的两条关键规则

代码就像下一个维护代码的人是一个知道你住在哪里的杀人狂。 最小惊奇原则。 即使您的非模板版本也违反了这些规则。通过对不相关的类W的隐藏转换来计算a1+a2,该类的运算符+返回的是a而不是W。这不是最令人惊讶的原则。至少可以说,这是惊人的。为什么您不认为显式转换是更好的方法


依赖于参数的查找是一个非常强大的工具,但是与此功能相关的问题很多。将ADL与自动类型转换相结合,问题就会扩大。将它与自动类型转换和模板结合在一起,你会陷入混乱。标准委员会认为足够了。除非将转换明确化,否则无法执行您想要执行的操作。

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

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

更新: 该问题已用其他信息编辑

我试图避免这种解决方案: a3=Wa1+Wa2

你到底为什么要避免这种情况?你的这段代码违反了编程的两条关键规则

代码就像下一个家伙一样 维护你的密码是一个知道你住在哪里的杀人狂。 最小惊奇原则。 即使您的非模板版本也违反了这些规则。通过对不相关的类W的隐藏转换来计算a1+a2,该类的运算符+返回的是a而不是W。这不是最令人惊讶的原则。至少可以说,这是惊人的。为什么您不认为显式转换是更好的方法


依赖于参数的查找是一个非常强大的工具,但是与此功能相关的问题很多。将ADL与自动类型转换相结合,问题就会扩大。将它与自动类型转换和模板结合在一起,你会陷入混乱。标准委员会认为足够了。除非将转换显式化,否则无法执行您想要执行的操作。

所以编译器是正确的,但这正是我想要避免的。也许有另一种方法可以通过模板包装器实现a3=a1+a2?我已经更新了我的问题。你的答案仍然适用吗?@PiotrNycz:没有;您仍然依赖隐式转换。为什么不提供一个带有is_constructible_from的SFINAE ed模板呢?你是说模板T操作符+W,W吗?@PiotrNycz:不,我考虑的更多,字面上依赖于std::is_constructible…所以编译器是对的,但这是我想要避免的。也许有另一种方法可以通过模板包装器实现a3=a1+a2?我已经更新了我的问题。你的答案仍然适用吗?@PiotrNycz:没有;您仍然依赖隐式转换。为什么不提供一个带有is_constructible_from的SFINAE ed模板呢?你是说模板T操作符+W,W吗?@PiotrNycz:不,我想的更多,字面上依赖于std::is_constructible…在调用点上可见的函数模板没有注意到你之前的更新,这意味着什么。我同意,但是。。。这不是真正的代码。我只是用它作为例子来说明问题,而不是所有的环境。考虑到这里的海报总是被要求发布一个只显示问题的代码,但是他们却被愚蠢的编程批评了…我会记得补充一点,代码只是一个人工的说明。如果在callI上看到一个函数模板的名称,那意味着什么?我之前没有注意到你的更新。我同意,但是。。。这不是真正的代码。我只是用它作为例子来说明问题,而不是所有的环境。考虑到这里的海报总是被要求发布一个只显示问题的代码,但是他们却被愚蠢的编程批评了…我会记得补充一点,代码只是人工的说明。
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;