隐式转换运算符优先级

时间:2017-02-15 14:25:02

标签: c++ casting operator-overloading standards overload-resolution

在以下代码段中(live on coliru):

#include <iostream>
#include <string>

int main()
{
    struct S {
        operator bool        () const { return false; }
        operator std::string () const { return "false"; }
    } s;
    std::cout << s << "\n"; // outputs 0
}

编译器如何选择以选择boolstd::string的隐式转换?

我的假设是,在这种情况下,它可能纯粹是std::basic_ostream::operator<<的不同风格的声明顺序,但它是否全部?标准是否说明了选择特定的隐式转换?

2 个答案:

答案 0 :(得分:11)

回想一下,std::string不是一个独立的类型,它实际上是一个类模板特化 - std::basic_string<char>。非常重要的细节是,流std::string的潜在重载不会带std::string const&个参数,而function template推导出std::basic_string const&

template <class CharT, class Traits, class Allocator>
std::basic_ostream<CharT, Traits>& 
    operator<<(std::basic_ostream<CharT, Traits>& os, 
               const std::basic_string<CharT, Traits, Allocator>& str);

模板扣除从不考虑转化。名称查找将找到此功能模板,然后由于扣减失败而丢弃为不可行。对于任何此类类型,S都不是basic_string<CharT, Traits, Allocator>,所以我们已经完成了。唯一可行的流运算符将是所有整数运算符,其中bool是最佳匹配。

如果特别是签名函数:

std::ostream& operator<<(std::ostream&, std::string const& );    

然后调用将是模棱两可的 - 您将获得两个等效排名的用户定义转换。

使用我们自己的函数而不是operator<<的百万次重载很容易验证:

void foo(bool ); // #1
void foo(std::string ); // #2

void bar(bool );  // #3
template <class C, class T, class A>
void bar(std::basic_string<C,T,A> ); // #4

foo(S{}); // error: ambiguous
bar(S{}); // calls #3

答案 1 :(得分:2)

ostream& operator<<( bool value );

std::ostream的成员函数。另一方面:

std::ostream& operator<<(std::ostream& os, const std::string& str);

是一个独立的函数 - ,它实际上被声明为模板。对S的引用与任何模板都不匹配 - 因此不考虑模板扩展。

可以唯一地确定应选择哪个过载,但我建议您不要这样做。

a)这始终是标准中棘手的一个角落(因此您可能会遇到编译器错误;

b)未来的开发人员总会发现难以阅读的代码。

我的建议是完全避免这个问题,只需要明确转换运算符,或者为它们指定to_bool()to_string()之类的名称。