C++ 标准中是否有任何计划来解决初始化列表构造函数的不一致问题?
are there any plans in C++ standard to address inconsistency of initializer list constructors?
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
(澄清一下,我的意思是 <int>
构造函数是一个初始化列表构造函数,而 <string>
是 而不是 。)
int
的情况匹配初始化列表构造函数,而 string
的情况不匹配。这有点难看,而且经常引起麻烦。在 Scott Meyers 的 Effective Modern C++ 的早期章节(第 7 项)中也提到了这一点,他将其描述为标准中有点令人不快的部分,无论何时初始化列表构造函数可用,编译器都会跳来跳去尝试匹配它,使其优先于所有其他构造函数。
当然,通过将u{3}
更改为u(3)
可以很容易地解决int 大小写问题,但这不是重点。
这是理想的行为吗? C++ 标准委员会是否有任何讨论或计划来解决这种类型的歧义/不愉快?一个例子是要求像这样调用初始化列表构造函数:vector<int> u({3})
,目前已经合法。
首先是统一初始化器,它被引入是为了解决语言的歧义。这个问题被称为 Most Vexing Parse 与声明变量相关,由 "round" () 括号初始化。 MVP是一种歧义解决,代码类似如下:
class SomeInitClass;
void bleh()
{
int foo(SomeInitClass());
}
foo
这里其实是一个函数的原型,它的参数是一个return是一个Bar的函数,foo
函数的return值是一个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++ 语言具有重要意义,它采用隐藏其语法背后的真实值和操作数量的概念。
这不是不一致,至少不是主要的。 vector<string>
和 vector<int>
不相同 class 并且没有相同的构造函数,因为它们不是模板 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。
Has there been any discussion or plans by the C++ standard committee to fix this type of ambiguity / unpleasantness?
自 C++11 以来,对初始化进行了许多修复。例如,您最初无法使用列表初始化 (CWG 1467). This really minor fix broke some code in an undesirable way that lead to a new issue to refine that previous fix to undo it if there's an initializer_list
constructor (CWG 2137) 复制构造聚合。在没有很多意想不到的后果和破坏代码的情况下,很难触及这些子句中的任何内容,即使在小情况下也是如此。我怀疑将来是否会推动对初始化进行任何类型的 large 更改。在这一点上,代码破坏的数量将是巨大的。
最好的解决办法就是了解初始化的缺陷并谨慎操作。我的经验法则是仅在我有意需要 {}
提供的行为时才使用 {}
,否则使用 ()
。
请注意,这与更广为人知的陷阱并没有什么不同:
vector<int> a{10}; // vector of 1 element
vector<int> b(10); // vector of 10 elements
One example would be requiring initializer list constructors to be called like this: vector<int> u({3})
, which is already currently legal.
您遇到了与之前相同的问题,原因相同:
vector<int> u({3}); // vector of one element: 3
vector<string> v({3}); // vector of three elements: "", "", and ""
即使您需要前者(这是不可能的),您也不能让后者成为病态的。
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
(澄清一下,我的意思是 <int>
构造函数是一个初始化列表构造函数,而 <string>
是 而不是 。)
int
的情况匹配初始化列表构造函数,而 string
的情况不匹配。这有点难看,而且经常引起麻烦。在 Scott Meyers 的 Effective Modern C++ 的早期章节(第 7 项)中也提到了这一点,他将其描述为标准中有点令人不快的部分,无论何时初始化列表构造函数可用,编译器都会跳来跳去尝试匹配它,使其优先于所有其他构造函数。
当然,通过将u{3}
更改为u(3)
可以很容易地解决int 大小写问题,但这不是重点。
这是理想的行为吗? C++ 标准委员会是否有任何讨论或计划来解决这种类型的歧义/不愉快?一个例子是要求像这样调用初始化列表构造函数:vector<int> u({3})
,目前已经合法。
首先是统一初始化器,它被引入是为了解决语言的歧义。这个问题被称为 Most Vexing Parse 与声明变量相关,由 "round" () 括号初始化。 MVP是一种歧义解决,代码类似如下:
class SomeInitClass;
void bleh()
{
int foo(SomeInitClass());
}
foo
这里其实是一个函数的原型,它的参数是一个return是一个Bar的函数,foo
函数的return值是一个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++ 语言具有重要意义,它采用隐藏其语法背后的真实值和操作数量的概念。
这不是不一致,至少不是主要的。 vector<string>
和 vector<int>
不相同 class 并且没有相同的构造函数,因为它们不是模板 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。
Has there been any discussion or plans by the C++ standard committee to fix this type of ambiguity / unpleasantness?
自 C++11 以来,对初始化进行了许多修复。例如,您最初无法使用列表初始化 (CWG 1467). This really minor fix broke some code in an undesirable way that lead to a new issue to refine that previous fix to undo it if there's an initializer_list
constructor (CWG 2137) 复制构造聚合。在没有很多意想不到的后果和破坏代码的情况下,很难触及这些子句中的任何内容,即使在小情况下也是如此。我怀疑将来是否会推动对初始化进行任何类型的 large 更改。在这一点上,代码破坏的数量将是巨大的。
最好的解决办法就是了解初始化的缺陷并谨慎操作。我的经验法则是仅在我有意需要 {}
提供的行为时才使用 {}
,否则使用 ()
。
请注意,这与更广为人知的陷阱并没有什么不同:
vector<int> a{10}; // vector of 1 element
vector<int> b(10); // vector of 10 elements
One example would be requiring initializer list constructors to be called like this:
vector<int> u({3})
, which is already currently legal.
您遇到了与之前相同的问题,原因相同:
vector<int> u({3}); // vector of one element: 3
vector<string> v({3}); // vector of three elements: "", "", and ""
即使您需要前者(这是不可能的),您也不能让后者成为病态的。