英文:
Coderpad's new C++ example is cryptic: meaning of `auto` and `const char* const&`
问题
-
在
auto words = { "Hello, ", "World!", "\n" };
中,变量words
的类型是std::initializer_list<const char*>
。 -
在各种
for
循环中,变量word
的类型是const char*
,不是const char* const&
。const char*
表示指向常量字符的指针,而const char* const&
表示引用到常量指针指向的常量字符。使用const char*
更简单,并且在这种情况下更常见。你的最后一个示例代码中就使用了const char*
,而不是const char* const&
。
英文:
Two questions about the code below:
- What is the type of the variable
words
inauto words = { "Hello, ", "World!", "\n" };
? - What are the types of the variable
word
in the variousfor
loops?
Coderpad's (an online coding tool used to write code live in front of an interviewer--test it yourself at https://app.coderpad.io/sandbox and then select the language at the left) default C++ example used to be this:
#include <iostream>
using namespace std;
// To execute C++, please define "int main()"
int main() {
auto words = { "Hello, ", "World!", "\n" };
for (const string& word : words) {
cout << word;
}
return 0;
}
It's output is:
> Hello, World!
The meaning of auto
is pretty complicated there. words
is of type std::initializer_list<std::string>
(right?--I mean, doing the replacement just below does run), so you can do this:
// replace this
auto words = { "Hello, ", "World!", "\n" };
// with this
std::initializer_list<std::string> words = { "Hello, ", "World!", "\n" };
This is why I hate auto
so much. I think this Coderpad example is terrible.
Well, it just got worse. Their new C++ example within the last several months is now:
#include <iostream>
using namespace std;
// To execute C++, please define "int main()"
int main() {
auto words = { "Hello, ", "World!", "\n" };
for (const char* const& word : words) {
cout << word;
}
return 0;
}
The type of words
now seems to be std::initializer_list<const char*>
, right? That runs.
But, what the heck is const char* const&
in the for
loop? I'm not familiar with this. The website https://cdecl.org/, the common-person's translator of "C gibberish" to "English", says that const char* const& word
means:
> declare word as reference to const pointer to const char
I guess that makes sense. But, why a reference to a const pointer to const char instead of just a pointer to a const char?
Using for (const char* word : words) {
instead of for (const char* const& word : words) {
seems to work just fine too, and is much simpler.
Here's my final, more-explicit version which runs just fine too:
#include <iostream>
// To execute C++, please define "int main()"
int main() {
std::initializer_list<const char*> words = { "Hello, ", "World!", "\n" };
for (const char* word : words) {
std::cout << word;
}
return 0;
}
<sub>Note: Coderpad is a shared coding interview tool I type into when taking or giving an interview. It's not for learning; it's for interviewing. It shares the coding screen so two people can see the same code while interviewing remotely over a Zoom chat or phone call.</sub>
答案1
得分: 1
以下是您要翻译的内容:
-
变量
words
的类型在auto words = { "Hello, ", "World!", "\n" };
中是什么?答案是
std::initializer_list<const char*>
> 要从编译器中获取实际类型,请添加这行代码
struct {} words2 = words;
,然后查看错误消息:error: conversion from ‘std::initializer_list<const char*>’ to non-scalar type ‘main()::<unnamed struct>’ requested
。所以实际类型是std::initializer_list<const char*>
。这里有一个完整的示例,演示了这一点:
#include <iostream> // 要执行C++代码,请定义 "int main()" int main() { auto words = { "Hello, ", "World!", "\n" }; struct {} words2 = words; return 0; }
-
在各种
for
循环中,变量word
的类型是什么?在
for (const string& word : words)
中,它的类型是const string&
,所以const char*
类型转换为const string
引用。在以下情况中:
for (const char* const& word : words) { // 和(同样的事情): for (const auto& word : words) { // 和(同样的事情): for (auto const & word : words) {
… 它的类型是
const char* const&
,这是通过常量引用传递的const char*
。在这种情况下,与以下代码没有真正的优势:
for (const char* word : words) // 或(同样的事情): for (auto word : words)
… 其中
word
是一个const char*
,通过从初始化列表words
复制指针元素到变量word
来传递指针的副本(将const char*
指针元素从初始化列表words
复制到变量word
),因为复制指针与通过C++引用传递一样有效。然而,正如 @chris_se 在评论中所指出的:
for (const auto word : words) // 或(同样的事情): for (auto const word : words) // 或(同样的事情,但显式): for (const char* const word : words)
…与上面的略有不同,因为这里的
const
应用于整个auto
类型,而auto
类型是const char*
,使得const auto
或auto const
类型因此为const char* const
,这使得指针本身也是const
,而不仅仅是指向的内容是const
。正如 @chris_se 所表述的:> 至于你的最后一个代码块:从技术上讲,
for (const auto word : words)
的类型将是const char * const
而不是const char*
。这意味着在for
循环中,你可以让word
指向不同的C字符串(word = "Goodbye!";
),但在const auto word: words
变体中,由于指针本身也是const
,所以你不能这样做。还有来自 @NathanOliver 的评论,[已编辑以添加标点符号]:
> [在
const auto& word : words
中,] 变量word
的类型将是对const
的引用 <无论元素类型words
是什么>。在这种情况下,const auto&
将是const char* const&
,而const auto
将是const char* const
[因为words
中的元素类型已经是const char*
]。
深入了解C++中的“胡言乱语”和黑盒;通过“洞察”C++代码展开来看看编译器看到了什么
还有来自 @chris_se 的内容,在这里:
> 对于这些问题,请查看非常有用的C++洞察网站:https://cppinsights.io/s/5d143229
示例源代码:
#include <iostream>
void a()
{
auto words = { "Hello, ", "World!", "\n" };
for (auto const& word : words) {
std::cout << word;
}
}
void b()
{
auto words = { "Hello, ", "World!", "\n" };
for (auto const word : words) {
std::cout << word;
}
}
// 要执行C++代码,请定义 "int main()"
int main() {
a();
b();
return 0;
}
通过 https://cppinsights.io/ 提供的示例“洞察”和代码展开:
#include <iostream>
void a()
{
std::initializer_list<const char *> words = std::initializer_list<const char *>{ "Hello, ", "World!", "\n" };
{
std::initializer_list<const char *> & __range1 = words;
const char *const * __begin1 = __range1.begin();
const char *const * __end1 = __range1.end();
for(; __begin1 != __end1; ++__begin1) {
const char *const & word = *__begin1;
std::operator<<(std::cout, word);
}
}
}
void b()
{
std::initializer_list<const char *> words = std::initializer_list<const char *>{ "
<details>
<summary>英文:</summary>
Here's the best I could come up with, looking at feedback thus far. My questions are:
1. **What is the type of the variable `words` in `auto words = { "Hello, ", "World!", "\n" };`?**
The answer is `std::initializer_list<const char*>`
[@pts has a great tip to figure this out](https://stackoverflow.com/questions/75656427/coderpads-new-c-example-is-cryptic-meaning-of-auto-and-const-char-const/75656629#comment133473115_75656427):
> To get the actual type from the compiler, add the line `struct {} words2 = words;`, and look at the error message: `error: conversion from ‘std::initializer_list<const char*>’ to non-scalar type ‘main()::<unnamed struct>’ requested`. So the actual type is `std::initializer_list<const char*>`.
Here's a full example which does that:
```cpp
#include <iostream>
// To execute C++, please define "int main()"
int main() {
auto words = { "Hello, ", "World!", "\n" };
struct {} words2 = words;
return 0;
}
```
1. **What are the types of the variable `word` in the various `for` loops?**
In `for (const string& word : words) {` it is `const string&`, so the `const char*` types are converted to `const string` references.
In:
```cpp
for (const char* const& word : words) {
// and (same thing):
for (const auto& word : words) {
// and (same thing):
for (auto const & word : words) {
```
...it is `const char* const&`, which is a `const char*` passed by const reference.
There's no real advantage in this case over the following:
```cpp
for (const char* word : words)
// or (same thing):
for (auto word : words)
```
...in which `word` is a `const char*`, passed by copy of a pointer (copying `const char*` pointer elements from the initializer list `words` to the variable `word`), since copying a pointer is as efficient as passing by C++ reference.
As [pointed out by @chris_se in the comments](https://stackoverflow.com/questions/75656427/coderpads-new-c-example-is-cryptic-meaning-of-auto-and-const-char-const/75656629#comment133473370_75656629), however: this:
```cpp
for (const auto word : words)
// or (same thing):
for (auto const word : words)
// or (same thing, but explicit):
for (const char* const word : words)
```
...is slightly different from just above, as the `const` here gets applied to the whole `auto` type, and the `auto` type is `const char*`, making the `const auto` or `auto const` type therefore `const char* const`, which makes the _pointer itself_ also `const`, not just the contents it points to `const`. As @chris_se puts it:
> With regards to the last code block you have: technically `for (const auto word : words)` will have a type `const char * const` instead of `const char*`. This means that with `for (const char* word : words)` you can let `word` point to a different C string within the for loop (`word = "Goodbyte!";`), but you can't do that with the `const auto word: words` variant, as the pointer itself is also `const` there.
And [from @NathanOliver](https://stackoverflow.com/questions/75656427/coderpads-new-c-example-is-cryptic-meaning-of-auto-and-const-char-const/75656629#comment133473658_75656427) [edited for punctuation]:
> [In `const auto& word : words`,] the type [of the variable `word`] will be a reference to a `const` \<whatever element type `words` has>. In this case, `const auto&` would be `const char* const&`, and `const auto` would be `const char* const` [since the element type for elements in `words` is already `const char*`].
## Going further: understanding C++ gibberish and black boxes; gaining "insight" into C++ code expansion to see what the compiler sees
Also from @chris_se, [here](https://stackoverflow.com/questions/75656427/coderpads-new-c-example-is-cryptic-meaning-of-auto-and-const-char-const/75656629#comment133473287_75656427):
> Take a look at the very useful C++ insights website for these kinds of questions: https://cppinsights.io/s/5d143229
Example source:
```cpp
#include <iostream>
void a()
{
auto words = { "Hello, ", "World!", "\n" };
for (auto const& word : words) {
std::cout << word;
}
}
void b()
{
auto words = { "Hello, ", "World!", "\n" };
for (auto const word : words) {
std::cout << word;
}
}
// To execute C++, please define "int main()"
int main() {
a();
b();
return 0;
}
Example "insights" and code expansion provided by https://cppinsights.io/:
#include <iostream>
void a()
{
std::initializer_list<const char *> words = std::initializer_list<const char *>{"Hello, ", "World!", "\n"};
{
std::initializer_list<const char *> & __range1 = words;
const char *const * __begin1 = __range1.begin();
const char *const * __end1 = __range1.end();
for(; __begin1 != __end1; ++__begin1) {
const char *const & word = *__begin1;
std::operator<<(std::cout, word);
}
}
}
void b()
{
std::initializer_list<const char *> words = std::initializer_list<const char *>{"Hello, ", "World!", "\n"};
{
std::initializer_list<const char *> & __range1 = words;
const char *const * __begin1 = __range1.begin();
const char *const * __end1 = __range1.end();
for(; __begin1 != __end1; ++__begin1) {
const char *const word = *__begin1;
std::operator<<(std::cout, word);
}
}
}
// To execute C++, please define "int main()"
int main()
{
a();
b();
return 0;
}
Their default example:
Source:
#include <cstdio>
int main()
{
const char arr[10]{2,4,6,8};
for(const char& c : arr)
{
printf("c=%c\n", c);
}
}
Insight:
#include <cstdio>
int main()
{
const char arr[10] = {2, 4, 6, 8, '\0', '\0', '\0', '\0', '\0', '\0'};
{
const char (&__range1)[10] = arr;
const char * __begin1 = __range1;
const char * __end1 = __range1 + 10L;
for(; __begin1 != __end1; ++__begin1) {
const char & c = *__begin1;
printf("c=%c\n", static_cast<int>(c));
}
}
return 0;
}
答案2
得分: 0
从讨论中,似乎你正在指出语言与用户之间的互动以及反过来,而不仅仅是特定的技术细节。
例如,通过这些互动式站点。
我认为许多这些互动式站点都会影响学习体验。
我同意你展示的特定代码可能会导致混淆;它可能甚至无法通过代码审查或代码分析。
野外的C++代码通常很糟糕,包括书籍和代码片段。
我可以解释混淆的起因:在C++中,字符串、initializer_list迭代等。
但我认为更积极的策略是不要轻易使用这些在线易用工具,而是努力寻找能够帮助你解决语言中这些不太明显的难点的严肃工具。
不幸的是,“Fisher-Price”类型的“学习C++/Python等”界面并不提供这些工具,因为它们把易用性放在第一位,把正确性放在第二位。
这些额外的工具和配置选项(不幸地)对于开发至关重要。
我在谈论像clang-tidy
这样的工具,至少要控制编译标志(-Wall -Wextra
)。
看看当我使用需要激活才能编译任何新项目的标志时会发生什么:
https://godbolt.org/z/x5da7rfEe
你可能会从现代编译器警告中学到更多,而不仅仅是从专家那里。
此外,这个面向专家的站点链接到了讨论中提到的cppinsights(以及性能基准工具)。
英文:
From the discussion, it seems you are pointing out how the language interacts with users and vice versa more than any particular technical point.
For example, through these interactive sites.
I think many of these interactive sites bias the experience of learning.
I agree that the particular code you show can lead to confusion; it probably wouldn't even pass code review or linter analysis.
C++ code in the wild is usually bad, including books and snippets.
I could explain the origin of the confusion: strings in C++, initializer_list iteration, etc.
But I think a more proactive strategy is not buying into these easy-to-use tools online.
And instead, make an effort to find serious tools that help you with these easy-to-reach dark corners of the language.
Unfortunately, "Fisher-Price"-type interfaces to "learn C++/Python/etc." do not offer these tools because they put ease of use first and correctness second.
These extra tools and configuration options are (unfortunately) fundamental to development.
I am talking about tools like clang-tidy
and, at the least, controlling compiling flags. (-Wall -Wextra
).
See what happens when I use flags that need to be active to compile any new project:
https://godbolt.org/z/x5da7rfEe
You will probably learn more from modern compiler warnings than any expert.
Also, this experts-friendly site has links to cppinsights, mentioned in the discussion (and benchmark tools).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论