这里对Stack stringStack = "bottom";
不合法的情景解读有误
原文的说法是:
However, by language rules, you can't copy initialize (initialize using =) an object by passing a string literal to a constructor expecting a std::string. So you have to initialize the stack as follows:
Stack stringStack{"bottom"}; //Stack<std::string> deduced and valid
直译如是:
然而,根据语言规则,你无法通过传递一个字符串字面量来拷贝初始化(使用=初始化语法)一个期望传递std::string
的对象。因此,你不得不按下面的方式初始化:Stack stringStack{"bottom"}; //Stack<std::string> deduced and valid
但我觉着这里作者的说法也不太对,上面的Stack stringStack = "bottom";
之所以不合法,是因为类的初始化赋值语句实际上会调用copy constructor,但是copy constructor需要的是一个Stack<std::string>
(Deduction Guides推断的T
为std::string
),而const char [7]
("bottom"字面量是const char [7]
类型)无法转换成non-scalar typeStack<std::string>
我写了一段代码来验证:
template<typename T>
class Stack {
private:
std::vector<T> elems;
public:
Stack(const T& elem) : elems({elem}) {}
};
Stack(char const*) -> Stack<std::string>;
int main()
{
Stack stringStack = "bottom";
return 0;
}
用gcc 7.3.1编译报错如下:
$ g++ main.cpp -o main -std=c++17
main.cpp: In function ‘int main()’:
main.cpp:38:23: error: conversion from ‘const char [7]’ to non-scalar type ‘Stack<std::basic_string<char> >’ requested
Stack stringStack = "bottom";
^~~~~~~~
std::basic_string是std::string的alias
可以看到这里需要一个隐式转换的方法(即接受const char [7]的一个ctor但显然没有),所以编译失败。
但终归是能看到上面的Deduction Guide语句生效了,因为确实编译错误里认为stringStack是Stack<std::basic_string<char> >
,尽管"bottom"是const char [7]
,与const char*
有些区别,但依然可以被推导为std::string
。当我把“bottom”换成const char *str = "bottom"; Stack stringStack = str;
,再次编译,错误变成:
main.cpp: In function ‘int main()’:
main.cpp:36:23: error: conversion from ‘const char*’ to non-scalar type ‘Stack<std::basic_string<char> >’ requested
Stack stringStack = str;
^~~
也是符合预期的,因为被我手工decay成了const char *,Deduction Guide依然生效。
当把Deduction Guide语句删除后,并重新改为Stack stringStack = "bottom";
编译错误迥然不同了:
main.cpp: In instantiation of ‘Stack<T>::Stack(const T&) [with T = char [7]]’:
main.cpp:36:23: required from here
main.cpp:28:40: error: no matching function for call to ‘std::vector<char [7], std::allocator<char [7]> >::vector(<brace-enclosed initializer list>)’
Stack(const T& elem) : elems({elem}) {}
^
......
省略了后面一大段,只看前面就可以看到T
被推断为char[7]
,这里接受const T&
参数的ctor语法编译就不通过了。而如果是const char *str = "bottom"; Stack stringStack = str;
则可以编译通过,这也是符合模板推断预期的。
至于后面的花括号直接初始化语法当然是可行的(这里因为既不是aggregate class也没有initializer_list做参数的ctor,所以是匹配某个合适的ctor),这个就不展开了,实际上,用小括号语法直接调用构造器也可以完成const char*
->std::string
的推断:Stack stringStack("bottom");