为什么 std::string 似乎做了 std::is_convertible 说不可能的事情?

Why std::string seem to do something that std::is_convertible says isn't possible?

为什么 std::to_string 允许您将 double 转换为 std::stringstd::is_convertible_v<double,std::string>false?例如:

#include<iostream>
#include<type_traits>
#include<string>

int main()
{
    std::cout << "If double is "
      << (std::is_convertible_v<double,std::string> ? "" : "not ")
      << "convertible to std::string then why is it possible to do this? "
      << std::to_string(3.141592) << std::endl;
    return 0;
}

std::is_convertible_v<double,std::string>false 意味着你不能做 std::string(3.141592)(或者更准确地说,double 不能隐式转换为 std::string),它有与 std::to_string.

无关

is_convertible 是在问一个关于两种类型之间的语言关系的非常具体的问题:源类型能否经历 implicit conversion 到目标类型?而 std::string 作为类型没有隐式转换为 double,所以答案是否定的。

std::to_string 只是一个普通的旧函数。它不会在作为类型的 std::stringdouble 之间创建任何关系。是的,它在语义上将 double 转换为 string,但这不是 C++ 语言规则的隐式转换。对于C++而言,它只是一个函数。

C++ 无法提出这样的问题:“是否有某个函数可以在某处将类型 X 的对象语义转换为类型 Y 的对象?”如果这是您要尝试回答的问题,那么实际上并没有一种实用的方法来获得答案。


需要注意的是,隐式转换的概念非常特殊。隐式转换会在很多地方自动发生。因此,如果一种类型可以隐式转换为另一种类型,那么它就具有很强的意义。如果一个字符串可以隐式转换为双精度,那么这在逻辑上意味着任何采用双精度的函数也可以被赋予一个字符串。

许多语言认为这是合法的(尽管这些语言通常只使用鸭子类型,所以它们通常没有“接受双精度值的函数”的概念,只有“接受值的函数”) ; C++ 不是其中之一。如果你想进行string-to-double转换,C++让你拼出来。

同样,从一种类型到另一种类型的隐式转换被认为是相关类型的固有 属性。也就是说,要创建从 XY 的 user-defined 转换,XY 必须具有定义此转换的成员函数。第三方代码无法创建两种类型之间的隐式转换。

因为

std::string test() { return std::declval<double>(); }

不是 well-formed 代码,因为 double 不能隐式转换,这是 std::is_convertible 检查的定义的一部分。