我正在尝试编写一个自定义字符串类
MyString
,并从const char*
进行隐式转换。
除此之外,我想通过移位运算符将
MyString
输出到任何std::ostream
。更重要的是,我希望它输出到也支持移位运算符的自定义流类中。我正在尝试使用适用于 MyStream
的模板来解决这种情况,就像使用 std::ostream
一样。
然而,这很容易导致重载决策的含糊不清。以下示例总结了问题的要点。
#include <iostream>
#include <sstream>
struct MyString
{
// implicit constructors
MyString( const char* cstr ) {};
// some dummy c_str()
const char* c_str() const { return nullptr; };
};
// // This works just fine, as expected, ...
// inline std::ostream& operator<<( std::ostream& out, const MyString& str )
// {
// std::cout << str.c_str();
// return out;
// }
// ... but this one causes ambiguity !!!
template<typename S>
inline S& operator<<( S& out, const MyString& str )
{
std::cout << str.c_str();
return out;
}
int main()
{
std::cout << "1";
std::stringstream ss;
ss << "2";
std::cout << ss.str().c_str();
return 0;
}
上面的代码运行得很好。但是,如果我注释掉
std::ostream
运算符重载并尝试使用模板,那么运算符调用会突然变得不明确。
向
std::cout
的转变有效,但向 std::stringstream ss
的转变变得不明确。
问题:第一种情况没有歧义而第二种情况突然歧义的原因是什么?有哪些可能性可以解决这个问题?
我想保留
MyString
的隐式转换工具,并且我想使用移位运算符将此类对象转换为行为类似于标准流的 anything。
编辑:能否编译取决于编译器的严格程度。在 https://www.onlinegdb.com/online_c++_compiler 我明白了
main.cpp: In function ‘int main()’:
main.cpp:33:11: warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:
33 | ss << "2";
| ^~~
In file included from /usr/include/c++/11/iostream:39,
from main.cpp:1:
/usr/include/c++/11/ostream:611:5: note: candidate 1: ‘std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, const char*) [with _Traits = std::char_traits<char>]’
611 | operator<<(basic_ostream<char, _Traits>& __out, const char* __s)
| ^~~~~~~~
main.cpp:21:11: note: candidate 2: ‘S& operator<<(S&, const MyString&) [with S = std::__cxx11::basic_stringstream<char>]’
21 | inline S& operator<<( S& out, const MyString& str )
| ^~~~~~~~
12
std::ostream
是 std::stringstream
的基类型,定义为 std::basic_stringstream<char>
(请参阅继承图)。
实例化函数 operator<<(std::stringstream& out, const MyString& str)
与 函数 operator<<(std::ostream& out, const char*s)
冲突,因为编译器无法决定是将 const char*
参数转换为 MyString
还是将 std::stringstream
向上转换为 std::ostream
。
我们可以显式转换字符串参数以消除歧义,即
ss << MyString("2");