【问题标题】:are there any plans in C++ standard to address inconsistency of initializer list constructors?C++ 标准中是否有任何计划来解决初始化列表构造函数的不一致问题?
【发布时间】:2018-02-06 05:08:10
【问题描述】:

C++ 中的初始化列表构造函数经常会引起麻烦;例如

using std::vector;
using std::string;
vector<string> v{3}; // vector of three empty strings
vector<int> u{3}; // vector of one element with value 3

(澄清一下,我的意思是&lt;int&gt; 构造函数是初始化列表构造函数,而&lt;string&gt;不是。)

int case 匹配初始化列表构造函数,而 string case 不匹配。这有点难看,并且经常引起麻烦。在 Scott Meyers 的 Effective Modern C++ 的早期章节(第 7 项)中也提到了这一点,他将其描述为标准中有些令人不快的部分,即每当初始化列表构造函数可用时,编译器都会跳过去尝试匹配它,赋予它高于其他所有构造函数的优先级。

当然,通过将 u{3} 更改为 u(3) 可以轻松修复 int 大小写,但这不是重点。

这是可取的行为吗? C++ 标准委员会是否有任何讨论或计划来解决这种模棱两可/不愉快的问题?一个例子是要求初始化列表构造函数像这样调用:vector&lt;int&gt; u({3}),目前已经是合法的。

【问题讨论】:

  • 这是two opposing forces 造成的混乱。但是不能告诉你他们是否在讨论它。
  • 原本可以很容易地通过使 {n} 仅映射到类型 N 的列表来修复它,如果只是用于 std 容器,但机会已经过去了,我认为这将是一个修复它的重大突破。
  • 使用 () 并不总是合法的,请参阅 MVP
  • 显然这是fixing another "problem" 的结果,其中array&lt;int,2&gt; a = {1,2}; 是允许的,但array&lt;int,2&gt; a{1,2}; 是不允许的。现在那个是固定的,但是......

标签: c++ c++11 constructor initializer-list c++-standard-library


【解决方案1】:

C++ 标准委员会是否有任何讨论或计划来解决这种类型的歧义/不愉快?

自 C++11 以来,已对初始化进行了许多修复。例如,您最初无法使用列表初始化 (CWG 1467) 复制构造聚合。如果有initializer_list 构造函数(CWG 2137),这个非常小的修复以一种不受欢迎的方式破坏了一些代码,从而导致一个新的问题来改进以前的修复以撤消它。很难触及这些条款中的任何内容而不会产生很多意想不到的后果和破坏代码,即使在小情况下也是如此。我怀疑将来会推动对初始化进行任何类型的更改。在这一点上,代码破坏的数量将是巨大的。

最好的解决方案就是意识到初始化的陷阱并小心你正在做的事情。我的经验法则是只在我故意需要{} 提供的行为时使用{}s,否则使用()

请注意,这与以下更广为人知的陷阱没有任何不同:

vector<int> a{10}; // vector of 1 element
vector<int> b(10); // vector of 10 elements

一个例子是要求初始化列表构造函数像这样调用:vector&lt;int&gt; u({3}),这目前已经是合法的。

出于同样的原因,您遇到了同样的问题:

vector<int> u({3});    // vector of one element: 3
vector<string> v({3}); // vector of three elements: "", "", and ""

即使你需要前者(这是不可能的),你也不能让后者格式不正确。

【讨论】:

  • 目前的情况是由于他们必须在每一步都避免旧代码损坏的事实造成的。只有选项要么打破整个语法格式并创建全新和不同的东西,要么遵循 python 的路径并扩展现有规则。标准作家有兴趣选择第二个选项。
【解决方案2】:

First 是一个统一初始化器,它被引入到解决语言的歧义。这个问题被称为Most Vexing Parse,与声明变量有关,由“round”()括号初始化。 MVP 是一种类似于以下代码的歧义解决方法:

class  SomeInitClass;


void  bleh()
{
       int foo(SomeInitClass());
}

foo 这里实际上是一个函数原型,它的参数是一个返回 Bar 的函数,foo 函数的返回值是一个int。本质上,如果 something 看起来像原型,C++ 就会这样对待它。

int foo{SomeInitClass{}};

SomeInitClass{} 总是会创建一个临时的。 int foo{...} 总是会创建一个变量。

虽然这两行的工作方式不同:

vector<string> v{3}; // vector of three empty strings
vector<int> u{3}; // vector of one element with value 3

它们具有相同的语义,它们是变量的声明。它的这种工作方式(以及您可以声明以初始化列表作为参数的构造函数的事实)确实对 C++ 语言具有重要意义,C++ 语言采用了隐藏其语法背后的真实值和动作数量的概念。

这不是不一致,至少不是主要不一致。 vector&lt;string&gt;vector&lt;int&gt; 不是同一个类,也没有相同的构造函数,因为它们不是模板 std::vector 的相同实例。为避免混淆,可以使用别名并使用稍有不同的语法。

 StringCollecton v{3}; //three strings;
 IntCollection   u = {3}; // or {{3}}

当然,StringCollecton test = {3}; 在这种情况下不起作用,因为3 不是可以转换为正确存储类型的字面量。

因为你在声明中只能有一个初始化器,所以设置创建的字符串容器的值看起来像这样:

std::vector<std::string> test{3, {"string"}}; // all values initialized by "string"

std::vector<std::string> test{{"string1","",""}}; // constructor introduced in C++11

虽然我可以很懒惰并忽略最里面的大括号,但语法糖允许我表明它是里面的 initializer_list。

【讨论】:

  • 抱歉,我看不出这如何以任何方式回答问题。
  • 他误用了这个词,他的意思显然是不一致。
  • 是的,std::vector&lt;std::string&gt;std::vector&lt;int&gt; 是两个不同的类。然而,一个不错的假设是,从std::vector 模板生成的任何类都会以相同的方式运行。让 std::vector&lt;X&gt;{3} 构造一个包含 3 个元素的向量,而 std::vector&lt;Y&gt;{3} 构造一个包含 1 个元素的向量是该语言的主要失败。我不在乎它是否对编译器完全有意义。编译器不编写代码。人类编写代码。如果语法很容易允许没有意义的代码或普通程序员混淆的代码,那么这就是该语言的主要缺点。
  • 我的主要语言是 C++,我喜欢它。但我仍然可以看到它的缺点。
  • DV,因为它不直接回答提出的问题。
猜你喜欢
  • 2020-06-21
  • 2023-02-07
  • 2017-03-07
  • 1970-01-01
  • 2016-07-19
  • 2016-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多