P38-Deduction Guides
Opened this issue · 7 comments
这里对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");
此外,后面这段语法我也没太理解作者的意思。
Note that, if in doubt, class template argument deduction copies. After declaring stringStack as Stackstd::string the following initializations declare the same type instead of initializing a stack by elements that are string stacks.
前半句没get到意思,后面应该是说下面的那几种初始化语句都是通过调用copy constructor定义了同样的type,倒是没什么疑问,只是总觉着作者说了什么我没有get到
感谢,受教了。
感觉作者的意思等于直接说出了结果:在copy initialize一个对象的时候,不能将string literal传递给一个接受std::string类型参数的构造函数。。暗含的意思是没有定义接受const char *作为参数的构造函数。。如果定义了接受const char *的构造函数,在执行Stack stringStack = “bottom”的时候,会先用Stack(const char *)生成一个临时对象,然后调用copy constructor完成copy initialize。。
而之所以stringStack("bottom")或者stringStack{“bottom”}可以,是因为此处已经明确了是执行一个构造函数的调用,编译器会帮忙完成"bottom"到std::string("bottom")的转换。。
之所以stringStack = “bottom”不可以,是因为在没有Stack(const char *)构造函数的情况下,需要执行两部隐式转换,一步是const char *到std::string. 另一步是std::string 到Stack。但是这种两步的转换不被编译器允许(C++ Primer第五版7.5.4节)。
不知道上述分析和你的意思是不是一致?等达成一致我去更新翻译内容。
感谢,受教了。
感觉作者的意思等于直接说出了结果:在copy initialize一个对象的时候,不能将string literal传递给一个接受std::string类型参数的构造函数。。暗含的意思是没有定义接受const char *作为参数的构造函数。。如果定义了接受const char *的构造函数,在执行Stack stringStack = “bottom”的时候,会先用Stack(const char *)生成一个临时对象,然后调用copy constructor完成copy initialize。。
而之所以stringStack("bottom")或者stringStack{“bottom”}可以,是因为此处已经明确了是执行一个构造函数的调用,编译器会帮忙完成"bottom"到std::string("bottom")的转换。。
之所以stringStack = “bottom”不可以,是因为在没有Stack(const char *)构造函数的情况下,需要执行两部隐式转换,一步是const char *到std::string. 另一步是std::string 到Stack。但是这种两步的转换不被编译器允许(C++ Primer第五版7.5.4节)。
不知道上述分析和你的意思是不是一致?等达成一致我去更新翻译内容。
理解是一致的。另外补充一句stringStack("bottom")
或者stringStack{“bottom”}
有效是在Deduction Guide(const char *->std::string)
的前提下,否则会直接推断成const char *
或const char [7]
(传值或传引用),就谈不上到std::string
的变化了,当然了,如果是直接指定Stack<std::string> stk("bottom")
;或是Stack<std::string> stk{"bottom"};
就不需要了
感谢,受教了。
感觉作者的意思等于直接说出了结果:在copy initialize一个对象的时候,不能将string literal传递给一个接受std::string类型参数的构造函数。。暗含的意思是没有定义接受const char *作为参数的构造函数。。如果定义了接受const char *的构造函数,在执行Stack stringStack = “bottom”的时候,会先用Stack(const char *)生成一个临时对象,然后调用copy constructor完成copy initialize。。
而之所以stringStack("bottom")或者stringStack{“bottom”}可以,是因为此处已经明确了是执行一个构造函数的调用,编译器会帮忙完成"bottom"到std::string("bottom")的转换。。
之所以stringStack = “bottom”不可以,是因为在没有Stack(const char *)构造函数的情况下,需要执行两部隐式转换,一步是const char *到std::string. 另一步是std::string 到Stack。但是这种两步的转换不被编译器允许(C++ Primer第五版7.5.4节)。
不知道上述分析和你的意思是不是一致?等达成一致我去更新翻译内容。理解是一致的。另外补充一句
stringStack("bottom")
或者stringStack{“bottom”}
有效是在Deduction Guide(const char *->std::string)
的前提下,否则会直接推断成const char *
或const char [7]
(传值或传引用),就谈不上到std::string
的变化了
谢谢,我是用实例化之后的版本做的测试。晚上更新一下翻译。到时候烦请再帮忙review一下。
感谢,受教了。
感觉作者的意思等于直接说出了结果:在copy initialize一个对象的时候,不能将string literal传递给一个接受std::string类型参数的构造函数。。暗含的意思是没有定义接受const char *作为参数的构造函数。。如果定义了接受const char *的构造函数,在执行Stack stringStack = “bottom”的时候,会先用Stack(const char *)生成一个临时对象,然后调用copy constructor完成copy initialize。。
而之所以stringStack("bottom")或者stringStack{“bottom”}可以,是因为此处已经明确了是执行一个构造函数的调用,编译器会帮忙完成"bottom"到std::string("bottom")的转换。。
之所以stringStack = “bottom”不可以,是因为在没有Stack(const char *)构造函数的情况下,需要执行两部隐式转换,一步是const char *到std::string. 另一步是std::string 到Stack。但是这种两步的转换不被编译器允许(C++ Primer第五版7.5.4节)。
不知道上述分析和你的意思是不是一致?等达成一致我去更新翻译内容。理解是一致的。另外补充一句
stringStack("bottom")
或者stringStack{“bottom”}
有效是在Deduction Guide(const char *->std::string)
的前提下,否则会直接推断成const char *
或const char [7]
(传值或传引用),就谈不上到std::string
的变化了谢谢,我是用实例化之后的版本做的测试。晚上更新一下翻译。到时候烦请再帮忙review一下。
无异议,感谢译者呕心沥血无私奉献
比较有趣的issue,reopen供更多人探讨