【问题标题】:Why does this C++ member initializer list not work?为什么这个 C++ 成员初始化器列表不起作用?
【发布时间】:2016-11-08 00:52:56
【问题描述】:

C++ in Plain English, Second Edition(1999 年)中,书中有一节说“=”可用于在调用正确的构造函数时进行初始化。就像给定一个类 CStr 和一个构造函数 CStr(char*),这本书是这样说的:

同样,最终声明调用构造函数CStr(char*):

CStr name3 = "Jane Doe";

我想用std::string 构造函数尝试同样的事情。我的部分测试代码是这样的:

using namespace std;
class CStr {
private:
    string name;
public:
    CStr(string s): name(s) {}
};

int main() {
    CStr name3 = "Jane Doe";
}

但是,当我编译时,我在 name3 初始化中遇到错误:

“请求从 'const char [9]' 转换为非标量类型 'CStr'”。

为什么将 CStr::name 初始化为字符串 s = "Jane Doe" 不起作用?像string nameTest{"Jane Doe"}; 这样的字符串测试有效,所以我认为这也可以。也许这本书很旧(这是我现在唯一的一本书),但我认为错误更多的是我自己。

【问题讨论】:

  • 顺便说一句,自 99 年以来,C++ 发生了很大变化。从那时起,两个新标准发布了,它们添加了大量新功能并删除了一些过时的功能。
  • 实际上,我的桌子上还有那本书的副本。非常有用的书,可作为 C 标准库的快速参考。
  • 顺便说一句,您可以将字符串文字与 CStr(char *) 构造函数一起使用的时代已经一去不复返了。字符串文字不再可转换为 char * 类型,只能转换为 const char *

标签: c++ string initialization


【解决方案1】:

您的书很旧,但基本正确[1]。请注意"Jane Doe" 不是std::string,而是const char[9](并且可能衰减为const char*)。所以对于CStr name3 = "Jane Doe";,需要两个用户定义的转换,(即const char* -> std::stringstd::string -> CStr),这是不允许在一个implicit conversion中进行的。

这也表明,如果CStr 的构造采用const char* 作为其参数,CStr name3 = "Jane Doe"; 将可以正常工作,因为它只需要一次用户定义的转换。

您可以通过添加显式转换来减少一个:

CStr name3 = std::string("Jane Doe");

或直接使用string literal(C++14 起),其类型为std::string

CStr name3 = "Jane Doe"s;

为什么将 CStr::name 初始化为字符串 s = "Jane Doe" 不起作用?像string nameTest{"Jane Doe"}; 这样的字符串测试有效,所以我认为这也可以。

您的问题还不够清楚,无论如何,std::string nameTest{"Jane Doe"}; 有效,因为(取决于您的误解)(1)这里只需要一次隐式转换(const char* -> std::string;(2)@987654345 @ 是直接初始化。

正如@LightnessRacesinOrbit 评论的那样,direct initialization(即CStr name3("Jane Doe")CStr name3{"Jane Doe"}(C++11 起))可以正常工作,而CStr name3 = "Jane Doe";copy initialization,它们在某些方面是不同的:

此外,复制初始化中的隐式转换必须 直接从初始化器产生 T ,同时,例如 直接初始化期望从 T 的构造函数参数的初始值设定项。

struct S { S(std::string) {} }; // implicitly convertible from std::string
S s("abc"); // OK: conversion from const char[4] to std::string
S s = "abc"; // Error: no conversion from const char[4] to S
S s = "abc"s; // OK: conversion from std::string to S

这意味着,对于复制初始化,参数Jane Doe,即const char*,必须直接转换为CStr;因为需要两次用户定义的转换,所以代码被拒绝。对于直接初始化,可以将Jane Doeconst char*)转换为CStr的构造函数的参数,即先std::string,然后调用CStr::CStr(std::string)构造对象。


[1] "Jane Doe" 是 c 风格的 string literal,它是 const,从 C++11 开始,将其分配给 char* 是非法的,例如

char * pc = "Jane Doe";         // illegal
const char * pcc = "Jane Doe";  // fine

【讨论】:

  • CStr name3("Jane Doe")CStr name3{"Jane Doe"}
  • 您的答案很好,但缺少的部分是这是复制初始化与其他类型不同的方式之一。
  • @LightnessRacesinOrbit OP 的误解对我来说不够清楚;无论如何我试图添加相关的解释。
  • @songyuanyao 这清除了一切,谢谢。您对转换的注释非常具有描述性。我没有过多考虑这个错误,我快速假设“Jane Doe”是std::string。我的困惑是:我认为“Jane Doe”是std::string。我试过string nameTest{"Jane Doe"}。我的想法是,“这行得通。我基本上在做string nameTest = "Jane Doe"。因此,在构造函数中,为什么string s = "Jane Doe" 不起作用?我现在看到“Jane Doe”不是std::string,因此有没有匹配的构造函数开始。
猜你喜欢
  • 2011-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多