为什么编译器只能在某些情况下将char *隐式转换为std :: string

4

以下内容可以正常工作:

struct WithString {
  WithString(std::string){};
};

void takeString(std::string){}

//implicit conversions:
takeString("hello");
WithString("hello");

但这个却不行:
WithString makeWithString() { return "hello";}

// error: no viable conversion from returned value of type 'const char [6]'...

如果在前两种情况下“hello”被隐式转换为std::string,那么为什么在最后一种情况下不能进行隐式转换呢?请注意,我没有将WithString构造函数指定为explicit,因此我希望进行这样的转换。
我可以通过以下方式使此行为正常工作:
struct WithString {
  WithString(std::string){};
  WithString(const char *){};
};

我对这种奇怪的情况很好奇。如果我猜测,我会说原因是在前两个工作的案例中,转换是在 const char *std::string 之间进行的,但在错误的情况下,这将需要一系列的两个转换,首先从 const char *std::string,然后从 std::stringWithString。所以也许这就是原因,但我不确定。
3个回答

6
我认为这是因为在前两个工作案例中,转换是在const char *和std::string之间进行的,但在错误案例中,这将需要两次转换,首先从const char *到std::string,然后从std::string到 WithString。所以也许这就是原因,但我不确定。
没错。
如果没有你的const char*构造函数重载,那么:
WithString makeWithString() { return "hello";}

需要两个用户定义的隐式转换;一个是转换为std::string,另一个是转换为WithString。这是不可能的。

然而,在这里只有一个隐式转换(转换为std::string):

takeString("hello");

同样的情况也适用于这里,因为随后对 WithString 的“转换”是显式的:

WithString("hello");

I can get the behavior to work by doing this:

struct WithString {
  WithString(std::string){};
  WithString(const char *){};
};

是的,这就是你应该做的。


3

您的方法:

WithString makeWithString() { return "hello";}

需要两个转换:隐式的const char *std::string的转换,然后构建一个WithString对象。在C++中最多只允许其中一个隐式发生。请参见此处的讨论:返回值上的非const拷贝构造函数和隐式转换

-1

请阅读C++标准中的隐式转换部分。我在VS 2015中尝试了以下代码,编译时没有出现错误。

#include <string>

struct WithString {
    WithString(std::string) {};
};

void takeString(std::string) {}

//implicit conversions:
void someFunc()
{
    takeString("hello");
    WithString("hello");
    WithString t = "hello";
}

WithString makeWithString() { return "hello"; }

看起来VS2015是错误的(将从const char *到string的转换视为标准转换)。 根据标准,以下代码应该可以工作,但在VS2015中会产生错误:

WithString makeWithString() { return "hello"s; }
WithString t = "hello"s;

另请参见复制初始化。在注释中,它明确将WithString t = "hello";称为错误。


如果MSVS 2015允许这个,那么它是有缺陷的。我很想知道您认为哪些C++11、C++14、C++17特性应该允许此程序? - Lightness Races in Orbit
确实,MSVS 2015 C++允许这样做。我想问题是const char到std:string是否是标准转换。如果是,则是正确的;如果不是(即用户定义的转换),则不是。在Visual Studio中将鼠标悬停在所有情况下的“hello”上会产生std::string::basic_string(const char_Ptr)。 - vdovydaitis3
是的,我在复制初始化下发现了额外的注释:复制初始化中的隐式转换必须直接从初始化器产生T,例如直接初始化期望从初始化器到T构造函数的参数的隐式转换。struct S { S(std::string) {} }; // 从std::string隐式转换而来 S s("abc"); // OK: 从const char[4]到std::string的转换 S s = "abc"; // 错误:没有从const char[4]到S的转换 S s = "abc"s; // OK: 从std::string到S的转换 - vdovydaitis3
单个值最多只能隐式应用一个用户定义的转换(构造函数或转换函数)。 - Lightness Races in Orbit

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