将隐式转换为std :: string

18

可能是重复问题:
当通过隐式转换为字符串流对象时出现重载解析失败

我知道这样做不是一个好主意,但我真的想知道下面的代码为什么无法编译(即为什么出现“没有可接受的转换”):

#include <iostream>
#include <string>


class Test
{
public:
    operator std::string () const;
};

Test::operator std::string () const
{
    return std::string("Test!");
}

int main ()
{
    std::string str = "Blah!";
    std::cout << str << std::endl;

    Test test;

    str = test;//implicitly calls operator std::string without complaining

    std::cout << str << std::endl;

    std::cout << test;//refuses to implicitly cast test to std::string

    return 0;
}

在Visual Studio 2010中,我遇到了这个错误:"error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'Test' (or there is no acceptable conversion)"

<<操作符是否会隐式地将std::string转换为其他类型以便使用它?如果是,我需要重载我的类中的哪个操作符才能使这个操作生效?我不相信我真的需要使用operator char *


1
еҰӮжһңжӮЁжғізӣҙжҺҘдҪҝз”Ёostreamsе’Ң<<дёҺTestе®һдҫӢдёҖиө·дҪҝз”ЁпјҢжӮЁеә”иҜҘиҰҶзӣ–жӯЈзЎ®зҡ„ж–№жі•пјҡhttp://msdn.microsoft.com/en-us/library/1z2f6c2k(v=vs.100).aspx - Jack
4
@Jack,我认为,这篇帖子的作者更关心为什么它不起作用,而不是如何使用完全不同的方法来修复它。 - Alessandro Teruzzi
5个回答

18

operator<<(std::basic_ostream&, std::basic_string)是一个函数模板,模板参数推导期间不考虑用户定义的类型转换。你需要为你的类重载operator<<

当然,另一种选项是使用强制类型转换。

std::cout << static_cast<std::string>(test);

2
"显式转换"是多余的。 - Pete Becker
并非所有的 operator<< 都是模板,但 string 的确是;这一点值得说明。 - Matthieu M.
“在模板参数推导期间,不考虑用户定义的转换”。有趣。所以,基本上,如果我定义operator std::basic_string<char, std::char_traits<char>, std::allocator<char> > () const而不是operator std::string () const;,它应该可以编译通过?不幸的是,它似乎也不喜欢。我只是想更好地理解模板,所以不用担心,这永远不会出现在真正的代码中 :) - Mihai Todor
2
实际上,这是因为在所需的隐式转换中有模板参数参与推导。 - Lightness Races in Orbit
@LightnessRacesinOrbit 感谢指出重复问题!我想现在慢慢开始理解了... 让我的大脑受伤。我会接受这个答案,基于你的评论。 - Mihai Todor

12

问题在于std::stringstd::basic_string<char>这个模板的特化形式,而要求重载的operator<<本身也是一个模板:

template<class charT, class traits, class Allocator>
basic_ostream<charT, traits>&
    operator<<(basic_ostream<charT, traits>&& os,
               const basic_string<charT,traits,Allocator>& str);
为了用作模板参数推导,用户定义类型必须是完全匹配的;不考虑转换。
您需要为您的类提供operator<<的重载,或显式转换为std::string

出于好奇,为了与上述情况完全匹配,用户定义的类型会是什么样子?我只是想更好地理解模板。 - Mihai Todor
@MihaiTodor:要完全匹配,它必须是std::basic_string的特化。 - Mike Seymour

5
一般来说,这取决于类的流插入运算符<<是具体函数还是模板函数。
如果<<是一个具体函数,则会找到重载并进行转换(只要不会产生歧义)。
#include <iostream>
using namespace std;

template< class CharType >
struct String {};

ostream& operator<<( ostream& stream, String<char> const& s )
{
    return (stream << "s");
}

struct MyClass
{
    operator String<char> () const { return String<char>(); }
};

int main()
{
    cout << "String: " << String<char>() << endl;
    cout << "MyClass: " << MyClass() << endl;
}

然而,使用<<作为函数模板时,模板匹配找不到匹配项,因此不会尝试通过用户定义的运算符进行转换。
#include <iostream>
using namespace std;

template< class CharType >
struct String
{
};

template< class CharType >
ostream& operator<<( ostream& stream, String< CharType > const& s )
{
    return (stream << "s");
}

struct MyClass
{
    operator String<char> () const { return String<char>(); }
};

int main()
{
    cout << "String: " << String<char>() << endl;
    cout << "MyClass: " << MyClass() << endl;       // !Oops, nyet! Not found!
}

在您的情况下,std::string实际上只是std::basic_string<char>的一个typedef
解决方案:为您的类定义<<操作符,或者如果您想避免头文件依赖(考虑构建时间),则定义转换为例如char const*的方法,或者最简单和我建议的方法是,将该转换命名为特定名称,以使其必须显式调用。
显式好过隐式。

+1 对于 明确是好的,并通过减少测试用例来展示模板效果! - Matthieu M.
所以,也许是一个愚蠢的问题:如果std::string只是typedef basic_string<char, char_traits<char>, allocator<char>>,并且我在我的Test类中定义了operator std::basic_string<char, std::char_traits<char>, std::allocator<char>> () const;,为什么它仍然不被认为是“完全匹配”?我的意思是,根据你(和其他人)说的,这应该使它编译通过,对吧?我真的很想理解背后的原因。 - Mihai Todor
2
@MihaiTodor:哦,参数推导试图将MyClass<<调用的实际参数类型)与std::basic_string<C,T,A>(形式参数类型),其中CTA是模板参数进行匹配。但是没有这些参数的选择可以将MyClass转换为精确匹配或任何类型的匹配。对于模板参数匹配,只考虑直接的精确匹配,而不考虑任何用户定义的转换。模板参数匹配支持的唯一“转换”是派生到基类。 - Cheers and hth. - Alf

1

你需要重载 operator<< 方法。

std::ostream & operator <<(std::ostream & os, const Test & t) {
    return os << std::string(t);
}

0

我认为你需要重载的运算符是"<<"。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接