Coderpad的新C++示例很神秘:“auto”和“const char* const&”的含义

1wnzp6jl  于 2023-03-09  发布在  其他
关注(0)|答案(2)|浏览(134)

关于下面代码的两个问题:

  1. auto words = { "Hello, ", "World!", "\n" };中变量words的类型是什么?
    1.在各种for循环中,变量word的类型是什么?
    Coderpad(一种在线编码工具,用于在面试官面前现场编写代码--在https://app.coderpad.io/sandbox上自己测试,然后在左侧选择语言)的默认C++示例曾经是这样的:
#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;
}

其输出为:
你好,世界!
auto的含义在这里非常复杂。words的类型是std::initializer_list<std::string>(对吗?--我的意思是,下面的替换确实会运行),因此您可以这样做:

// replace this
auto words = { "Hello, ", "World!", "\n" };

// with this
std::initializer_list<std::string> words = { "Hello, ", "World!", "\n" };

这就是为什么我这么讨厌auto的原因,我觉得这个Coderpad的例子太可怕了。
好吧,情况变得更糟了。他们最近几个月的新C++示例现在是:

#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;
}

words的类型现在看起来是std::initializer_list<const char*>,对吗?
但是,for循环中的const char* const&到底是什么?我对这个不太熟悉。网站https://cdecl.org/,普通人的“C gibberish”到“英语”的翻译,说const char* const& word的意思是:
将单词声明为常量引用常量字符指针
我想这是有道理的。但是,为什么引用一个指向const char的const指针而不是只引用一个指向const char的指针呢?
使用for (const char* word : words) {代替for (const char* const& word : words) {似乎也能很好地工作,而且要简单得多。
下面是我最后一个更明确的版本,它运行得也很好:

#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;
}

注:Coderpad是一个共享的编程面试工具,我在面试时输入。它不是用来学习的;它是用于面试的。它共享编码屏幕,这样两个人就可以在通过Zoom聊天或电话进行远程面试时看到相同的代码。

xytpbqjk

xytpbqjk1#

以下是我能想到的最好的建议,看看到目前为止的反馈。我的问题是:
1.auto words = { "Hello, ", "World!", "\n" };中变量words的类型是什么?
答案是std::initializer_list<const char*>
@pts有一个很好的提示来解决这个问题:
要从编译器获取实际类型,请添加struct {} words2 = words;行,然后查看错误消息:所以实际的类型是std::initializer_list<const char*>
下面是一个完整的例子:

#include <iostream>

// To execute C++, please define "int main()"
int main() {
  auto words = { "Hello, ", "World!", "\n" };
  struct {} words2 = words;

  return 0;
}

1.各种for循环中变量word的类型是什么?
for (const string& word : words) {中它是const string&,因此const char*类型被转换为const string引用。
在:

for (const char* const& word : words) {
// and (same thing):
for (const auto& word : words) {
// and (same thing):
for (auto const & word : words) {

...它是const char* const&,它是通过常量引用传递的const char*
在这种情况下,与以下情况相比没有真实的的优势:

for (const char* word : words)
// or (same thing):
for (auto word : words)

...其中wordconst char*,通过指针复制传递(将const char*指针元素从初始化器列表words复制到变量word),因为复制指针与通过C++引用传递一样有效。
然而,正如@chris_se在评论中指出的那样:这个:

for (const auto word : words)
// or (same thing):
for (auto const word : words)
// or (same thing, but explicit):
for (const char* const word : words)

......与上面稍有不同,因为这里的const应用于整个auto类型,并且auto类型是const char*,使得const autoauto const类型因此是const char* const,这使得 * 指针本身 * 也是const,而不仅仅是它指向const的内容。正如@chris_se所说:
关于您拥有的最后一个代码块:从技术上讲,for (const auto word : words)的类型是const char * const,而不是const char*,这意味着使用for (const char* word : words)时,可以让word指向for循环中的另一个C字符串(word = "Goodbyte!";),但使用const auto word: words变量时就不行了,因为指针本身也是const
@NathanOliver [编辑标点符号]:
[In const auto& word : words,]变量word的类型将是对const的引用。在这种情况下,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;
  }
}

// To execute C++, please define "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 *>{"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;
}

它们的默认示例:
资料来源:

#include <cstdio>

int main()
{
    const char arr[10]{2,4,6,8};

    for(const char& c : arr)
    {
      printf("c=%c\n", c);
    }
}

洞察力:

#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;
}
ijxebb2r

ijxebb2r2#

从讨论来看,你似乎是在指出语言是如何与用户互动的,反之亦然,而不是任何特定的技术点。例如,通过这些互动网站。我认为许多这些互动网站偏向于学习的体验。
我同意您所显示的特定代码可能会导致混淆;它甚至可能无法通过代码审查或linter分析。野生的C代码通常是糟糕的,包括书籍和代码片段。
我可以解释这种困惑的根源:但是我认为一个更积极的策略不是购买这些在线的易用工具,而是努力去寻找一些严肃的工具来帮助你解决这些容易触及的语言的黑暗角落。不幸的是,"Fisher-Price"-type接口来“学习C
/Python/etc.”不提供这些工具,因为它们把易用性放在第一位,正确性放在第二位。
不幸的是,这些额外的工具和配置选项是开发的基础,我说的是像clang-tidy这样的工具,至少是控制编译标志(-Wall -Wextra)。
看看当我使用需要激活的标志来编译任何新项目时会发生什么:

https://godbolt.org/z/x5da7rfEe
你可能比任何Maven都能从现代编译器警告中学到更多,而且,这个Maven友好的网站有cppinsights的链接,在讨论中提到过(和基准测试工具)。

相关问题