使用模板但不使用普通函数时由于隐式转换而导致歧义

问题描述 投票:0回答:1

我正在尝试编写一个自定义字符串类

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
c++ templates overload-resolution
1个回答
0
投票

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");

© www.soinside.com 2019 - 2024. All rights reserved.