C++ 操作数到标准运算符的隐式转换
C++ implicit conversions in operands to standard operators
我在理解 C++ 使用隐式转换的条件时遇到一些问题。假设我有一个 class:
class SomeClass
{
private:
int val;
public:
SomeClass(int i) :val{ i } {}
operator string() { return to_string(val); }
};
为什么在将此 class 与运算符一起使用时我需要转换为字符串?为什么它不隐式执行转换?
代码:
int main(void)
{
SomeClass sc = 4;
cout << (string)sc << endl; //prints 4, compiles fine
cout << sc << endl;//does not compile, no operator "<<" matches these operands
string str1 = sc;//compiles fine, performs cast
string str2 = "Base String" + sc;//does not compile, no operator "+" matches these operands
}
这个问题的学术性多于实际性,因为无论如何只使用强制转换更具可读性。
std::cout
不接受 std::string
.
它接受一个模板化的 std::basic_string
,要推导其参数。
有关详细信息,请参阅 here。
让我们考虑以下示例:
#include<string>
struct SomeClass {
operator std::string() { return ""; }
};
template <class CharT, class Traits, class Allocator>
void f(const std::basic_string<CharT, Traits, Allocator> &) {}
template <class CharT, class Traits, class Allocator>
void f(std::basic_string<CharT, Traits, Allocator> &) {}
int main(void) {
SomeClass sc{};
f((std::string)sc);
//f(sc);
}
这与您的情况类似。
当然,f(sc)
由于几乎相同的原因无法编译。
问题是要推导出模板参数 SomeClass
应该转换为 std::string
。为了能够将其转换为 std::string
,编译器应该猜测存在有效的重载并尝试使用它。无论如何,从它的角度来看,这样一个可行的函数并不存在,因为模板参数仍然需要推导,为了推导它们,编译器应该猜测 SomeClass
的重载存在于强制转换之后。不能,因为……等等。
我还没有检查过,但我敢打赌类似的东西也适用于 operator+
。
写cout << (string)sc
时用到的operator<<
overload是一个函数模板:
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);
这是因为 std::string
本身就是一个 class 模板,并且可以用 char
以外的其他类型进行实例化,并且如果将字符写入流,应该仍然可以打印相同的字符类型。事实上,这正是 std::wstring
所发生的情况,其中 CharT
是 wchar_t
而不是 char
。如果您使用合适的流,同样 operator<<
可以工作。
当您写 cout << sc
时,会考虑特定的重载。然后拒绝该重载,因为编译器无法推断出将哪种类型用于 CharT
和其他类型。如果它已经知道将执行转换,则它只能推断出类型,但转换尚未被考虑,它们只会在稍晚的阶段进行检查。
如果有一个非模板 operator<<
重载占用了 std::string
,那么它将起作用,sc
将被隐式转换。
我在理解 C++ 使用隐式转换的条件时遇到一些问题。假设我有一个 class:
class SomeClass
{
private:
int val;
public:
SomeClass(int i) :val{ i } {}
operator string() { return to_string(val); }
};
为什么在将此 class 与运算符一起使用时我需要转换为字符串?为什么它不隐式执行转换?
代码:
int main(void)
{
SomeClass sc = 4;
cout << (string)sc << endl; //prints 4, compiles fine
cout << sc << endl;//does not compile, no operator "<<" matches these operands
string str1 = sc;//compiles fine, performs cast
string str2 = "Base String" + sc;//does not compile, no operator "+" matches these operands
}
这个问题的学术性多于实际性,因为无论如何只使用强制转换更具可读性。
std::cout
不接受 std::string
.
它接受一个模板化的 std::basic_string
,要推导其参数。
有关详细信息,请参阅 here。
让我们考虑以下示例:
#include<string>
struct SomeClass {
operator std::string() { return ""; }
};
template <class CharT, class Traits, class Allocator>
void f(const std::basic_string<CharT, Traits, Allocator> &) {}
template <class CharT, class Traits, class Allocator>
void f(std::basic_string<CharT, Traits, Allocator> &) {}
int main(void) {
SomeClass sc{};
f((std::string)sc);
//f(sc);
}
这与您的情况类似。
当然,f(sc)
由于几乎相同的原因无法编译。
问题是要推导出模板参数 SomeClass
应该转换为 std::string
。为了能够将其转换为 std::string
,编译器应该猜测存在有效的重载并尝试使用它。无论如何,从它的角度来看,这样一个可行的函数并不存在,因为模板参数仍然需要推导,为了推导它们,编译器应该猜测 SomeClass
的重载存在于强制转换之后。不能,因为……等等。
我还没有检查过,但我敢打赌类似的东西也适用于 operator+
。
写cout << (string)sc
时用到的operator<<
overload是一个函数模板:
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);
这是因为 std::string
本身就是一个 class 模板,并且可以用 char
以外的其他类型进行实例化,并且如果将字符写入流,应该仍然可以打印相同的字符类型。事实上,这正是 std::wstring
所发生的情况,其中 CharT
是 wchar_t
而不是 char
。如果您使用合适的流,同样 operator<<
可以工作。
当您写 cout << sc
时,会考虑特定的重载。然后拒绝该重载,因为编译器无法推断出将哪种类型用于 CharT
和其他类型。如果它已经知道将执行转换,则它只能推断出类型,但转换尚未被考虑,它们只会在稍晚的阶段进行检查。
如果有一个非模板 operator<<
重载占用了 std::string
,那么它将起作用,sc
将被隐式转换。