隐式转换运算符优先级
Implicit conversion operator priority
在下面的一段代码中(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
}
编译器 如何选择 来隐式转换为 bool
而不是 std::string
?
我的假设是,在这种情况下,它可能纯粹是 std::basic_ostream::operator<<
的不同风格的声明顺序,但这就是全部吗?该标准是否说明了有关选择特定隐式转换的内容?
ostream& operator<<( bool value );
是std::ostream
的成员函数。另一方面:
std::ostream& operator<<(std::ostream& os, const std::string& str);
是一个独立的函数 - 它实际上被声明为一个模板。对 S
的引用与任何模板都不匹配 - 因此不考虑将其用于模板扩展。
可以唯一地确定应该选择哪个重载,但我建议您不要那样做。
a) 这始终是标准的棘手角落之一(因此您可能会遇到编译器错误;
b) 未来的开发者总是会发现代码难以阅读。
我的建议是通过使转换运算符显式化或给它们命名,例如 to_bool()
和 to_string()
.
来完全避免该问题
回想一下 std::string
不是独立类型,它实际上是 class 模板特化 - std::basic_string<char>
。非常重要的细节是流式传输 std::string
的潜在重载不采用 std::string const&
参数,它是推导出 std::basic_string const&
:
的 function template
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);
模板推导从不考虑转换。名称查找会找到这个函数模板,然后由于推导失败而将 at 丢弃为 non-viable。 S
不是任何此类类型的 basic_string<CharT, Traits, Allocator>
,所以我们完成了。唯一可行的流运算符将是所有整数运算符,其中 bool
是最佳匹配。
如果专门有一个带有签名的函数:
std::ostream& operator<<(std::ostream&, std::string const& );
那么调用将是不明确的 - 您将获得两个 user-defined 排名相同的转化。
这很容易通过使用我们自己的函数而不是 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
在下面的一段代码中(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
}
编译器 如何选择 来隐式转换为 bool
而不是 std::string
?
我的假设是,在这种情况下,它可能纯粹是 std::basic_ostream::operator<<
的不同风格的声明顺序,但这就是全部吗?该标准是否说明了有关选择特定隐式转换的内容?
ostream& operator<<( bool value );
是std::ostream
的成员函数。另一方面:
std::ostream& operator<<(std::ostream& os, const std::string& str);
是一个独立的函数 - 它实际上被声明为一个模板。对 S
的引用与任何模板都不匹配 - 因此不考虑将其用于模板扩展。
可以唯一地确定应该选择哪个重载,但我建议您不要那样做。
a) 这始终是标准的棘手角落之一(因此您可能会遇到编译器错误;
b) 未来的开发者总是会发现代码难以阅读。
我的建议是通过使转换运算符显式化或给它们命名,例如 to_bool()
和 to_string()
.
回想一下 std::string
不是独立类型,它实际上是 class 模板特化 - std::basic_string<char>
。非常重要的细节是流式传输 std::string
的潜在重载不采用 std::string const&
参数,它是推导出 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);
模板推导从不考虑转换。名称查找会找到这个函数模板,然后由于推导失败而将 at 丢弃为 non-viable。 S
不是任何此类类型的 basic_string<CharT, Traits, Allocator>
,所以我们完成了。唯一可行的流运算符将是所有整数运算符,其中 bool
是最佳匹配。
如果专门有一个带有签名的函数:
std::ostream& operator<<(std::ostream&, std::string const& );
那么调用将是不明确的 - 您将获得两个 user-defined 排名相同的转化。
这很容易通过使用我们自己的函数而不是 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