为什么参数匹配适用于非模板版本,但不适用于模板版本
Why argument matching works for non template version, but does not work for template version?
考虑这个非常简单的例子,其中我有一个模板包装类,为其定义了运算符:
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;
模板参数推导出现在重载解析之前。但是模板参数推导不考虑隐式转换,所以您的模板化操作符甚至从未被考虑过。
只有在选择了重载之后,才会应用隐式转换。
不过,你可以说A a3 = W(a1) + W(a2);
。
您的第一个例子失败了,因为标准规定它必须在使用模板时失败。依赖于参数的名称查找可能存在问题,而模板会加剧这些问题。解决此问题的一种方法是将模板排除在此类查找之外,除非您明确使用模板。以下是C++03标准的相关文本,第14.8.1节第6段:
对于简单函数名,即使函数名在调用范围内不可见,也会应用参数相关查找(3.4.2)。这是因为调用仍然具有函数调用的语法形式(3.4.1。如果没有这样的名称可见,则该调用的语法格式不正确,并且不应用依赖于参数的查找。如果某个这样的名称可见,则应用依赖于参数的查找,并且可能在其他命名空间中找到其他函数模板。
更新:
已使用其他信息对问题进行了编辑。
我正试图避免此解决方案:
A a3 = W(a1) + W(a2);
你为什么要避免这种情况?您的这段代码违反了编程的两个关键规则。
- 密码,就好像下一个维护你密码的人是一个知道你住在哪里的杀人狂
- 最小惊奇原则
即使您的非模板版本也会违反这些规则。通过对不相关类W
的(隐藏)转换来计算a1+a2
,其中operator+
返回A
而不是W
。这不是最不惊奇的原则。至少可以说,这是令人惊讶的你为什么不认为显式转换是更好的方法呢
依赖于参数的查找是一个非常强大的工具,但与这种功能相关的问题很多。将ADL与自动类型转换结合起来,问题就会扩大。再加上自动类型转换和模板,你就会一团糟。标准委员会决定适可而止。除非明确转换,否则无法执行您想要执行的操作。
- FLTK 2.0构建和演示,适用于VS2019的2011年左右的代码库
- C++17 - 使用自定义分配器的节点提取/重新插入 - 适用于 clang++/libc++,但不适用于 libstd
- "string.h"在构建适用于iOS的qt应用程序中找不到消息
- 适用于 WebView2 旧版本的示例应用程序
- 在 NVIDIA GEFORCE GTX 1050 上下载适用于 Windows 10 的 openCL 1.2
- __attribute__(优化(0))) 是否适用于"recursively"?
- 为什么 std::erase(std::erase_if) 不是适用于<algorithm>任何容器的模板?
- 使用一个参数的模板函数时出错(适用于 2)
- 使用 适用于 Android 和 iOS 的 tf-lite C++ API
- 为什么这适用于 G++ 而不是 CLANG?
- 适用于 macOS 的 Xcode 应用程序。这就是我设置从USB麦克风输入获取音频的方式。一年前工作,现在没有了。为什么
- 适用于 Linux 的 c++ 上的代理脚本
- 为什么我的 SFINAE 表达式不再适用于 GCC 8.2?
- Netbeans 8.1(适用于 C/C++)找不到我的编译器(gcc-6.0.0 开发版本)
- 适用于 x64 和 Win32 平台的正确库版本
- 如何查询IE浏览器版本,使其也适用于IE10
- 适用于Windows XP的版本帮助程序功能
- 重复符号仅适用于iOS模拟器版本
- 移动文件夹Windows c++:适用于Vista及以上版本,不包括XP
- C++:如何将%USERPROFILE%路径转换为文件路径(应该适用于Windows XP及更高版本)