问题A
给出下面的代码示例:
#include <iostream>
#include <string>
class LogStream {
public:
LogStream& operator<<(int x) {
std::cout << x;
return *this;
}
LogStream& operator<<(const char* src) {
std::cout << src;
return *this;
}
};
typedef char MyType[81];
template <typename OS>
OS& operator<<(OS &os, const MyType& data) {
return os << "my version: " << data;
}
// error: use of overloaded operator '<<' is ambiguous
// (with operand types 'LogStream' and 'char const[81]')
/* LogStream& operator<<(LogStream &os, const MyType& data) {
return os << "my version2: " << (const char*)data;
} */
struct Test {
int x;
MyType str;
};
template <typename OS>
OS& operator<<(OS &os, const Test& data) {
return os << "{ x: " << data.x << ", str: " << data.str << "}";
}
int main() {
Test t = { 33, "333" };
LogStream stream;
stream << t.str;
std::cout << std::endl;
stream << t;
}
实际输出
my version: 333
{ x: 33, str: 333}
预期输出
my version: 333
{ x: 33, str: my version: 333}
在线编译器:https://godbolt.org/z/6os8xEars
我的问题是:为什么第一个输出使用了我的专用版本MyType
,而第二个没有?
问题B
我有一些关于模板专门化的相关问题:
1.当需要隐式转换时,函数模板和常规函数之间的优先级是什么,例如:
struct MyType{};
template <typename T>
void test(T t, char (&data)[16]);
void test(MyType t, const char* data);
int main() {
MyType mt;
char src[16] = { "abc" };
test(mt, src);
}
1.是否有任何工具可以可视化重载解决过程,即使程序编译成功?是否有调试模板代码的方法?
1条答案
按热度按时间41zrol4v1#
主要问题的简短答复是:t不是const,但第二个运算符模板的Test参数是。因此,表达式
t.str
是MyType&
,但data.str
是const MyType&
:这种差异可能会影响重载解决方案,因为一个关键方面是将函数实参转换为相应形参类型所需的所谓implicit conversion sequence(ICS)。
不幸的是,重载解析并不是一件小事,因此有相当多的事情需要解包。对于表达式
stream << t.str
,可行函数和ICS如下所示:第二个版本被视为标识转换,因为
引用参数直接绑定到参数表达式是Identity或派生到基的转换
为了决定两个候选函数中的一个是否是更好的匹配,编译器将考虑可行函数及其转换序列的许多方面。在这种情况下,规则3a适用:
S1是S2的子序列,不包括左值变换。恒等转换序列被认为是任何其他转换的子序列
因此,第二个ICS更好,使模板版本成为最佳可行函数。
对于第二个输出:
在这种情况下,规则3a不适用,因为排除数组到指针的转换,两个ICS都不是另一个的适当子序列。其他规则都不适用,因此ICS无法区分。因此,非模板操作符现在是最佳可行函数:
1.或者,如果不是,则F1是非模板函数,而F2是模板特化
这也是为什么您注解掉的操作符会是模棱两可的。如果您也注解掉
stream << t;
行,它就不再有歧义了。此外,这是唯一一点,它的事项,其中一个重载是一个模板,当然除了要求它是一个有效的示例化。因此,在问题B1中,再次选择函数模板,因为它具有更好的ICS。
至于问题B2,我不知道有什么特定的工具,尽管可以从clang中得到这种输出。现在我使用编译器资源管理器来解决这样的问题。我大概知道规则,但你可以打赌,在回答这类问题之前,我必须仔细重读一遍。现在您已经有了这些解释,它应该给予您了解当您遇到过载问题时需要查找的(许多)东西。
关于更多的阅读,运算符重载规则的官方措辞在标准的over.match.best(https://eel.is/c++draft/over.match.best)部分。
**编辑:**我的首选解决方案是将“特殊”字符串类型 Package 在类中。但是,如果你真的必须使用C风格的char数组,你仍然可以通过引入一个单独的日志类来达到预期的效果: