Mat*_*vei 20 c++ vector initializer-list compiler-specific direct-initialization
我有以下代码:
#include <iostream>
#include <vector>
struct C {
int a;
C() : a(0) {}
C(int a) : a(a) {}
};
std::ostream &operator<<(std::ostream &os, const C &c) {
os << c.a;
return os;
}
using T = std::vector<C>;
int main() {
auto v = T({5});
for (const auto &a : v) {
std::cout << a << ", ";
}
std::cout << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
如果我使用 g++,它会打印:
#include <iostream>
#include <vector>
struct C {
int a;
C() : a(0) {}
C(int a) : a(a) {}
};
std::ostream &operator<<(std::ostream &os, const C &c) {
os << c.a;
return os;
}
using T = std::vector<C>;
int main() {
auto v = T({5});
for (const auto &a : v) {
std::cout << a << ", ";
}
std::cout << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
如果我使用 MS 编译器,它会打印:
5,
Run Code Online (Sandbox Code Playgroud)
为什么结果不同?
没有()around {5},结果当然是一样的。
Adr*_*ica 13
这可能是 g++ 编译器\xc2\xa7中的错误(而不是MSVC 编译器中的错误)。变量的类型v是(或者根据标准,应该是std::vector<C>)a ,包含 5 个默认初始化元素。
为什么?因为您使用直接初始化(圆括号)而不是列表初始化(大括号,如 中auto v = T{ 5 })。这种直接初始化意味着调用构造函数,并且没有std::vector<T>需要单个T参数的构造函数。
cppreference中提到了这种混乱的根源:
\n\n\n请注意,列表初始化构造函数 (10) 的存在意味着\n列表初始化和直接初始化会执行不同的操作:
\n
现在,您可能认为编译器应该使用列表初始化构造函数(就像 g++ 那样)\xe2\x80\xa6,但它不应该这样做!该构造函数的参数不是类型而是T类型std::initializer_list<T>;因此,要调用该构造函数(使用直接初始化),您需要auto v = T({ { 5 } });(注意额外的大括号组)。\xe2\x80\xa0
对于您的代码,auto v = T({ 5 });,最匹配的构造函数是链接的 cppreference 页面上的版本#3(默认为第二个和第三个参数),因此{ 5 }被视为size_t初始化为 的元素计数的值T()。事实上,根据同一页面,自 C++11 \xe2\x80\x93 以来,默认的第二个参数版本已被删除,因此您的调用的唯一匹配是构造函数版本 #4,它会产生相同的结果。
事实上,clang(它为您的代码提供与 MSVC 相同的结果)对此发出警告(请在 Compiler Explorer 上查看):
\n\n\n警告:标量初始值设定项周围的大括号 [-Wbraced-scalar-init]
\n
有多种方法可以强制初始化带有{5}成员的单元素向量。最简单的是使用列表初始化:
auto v = T{ { 5 } }; // Or even: auto v = T{ 5 };\nRun Code Online (Sandbox Code Playgroud)\n另一种方法是添加一个显式count参数:
auto v = T(1, { 5 }); // Or just: auto v = T(1, 5);\nRun Code Online (Sandbox Code Playgroud)\n\xc2\xa7在涉及编译器应遵循的规则以符合 C++ 标准时,我并不声称自己是权威,因此我准备接受这可能(罕见的)歧义标准,而不是 g++ 编译器中的错误或缺陷。
\n\xe2\x80\xa0请注意,虽然{ 5 } 可以用作有效值std::initializer_list<C>(就像 的情况一样auto v = T{ 5 };),但就函数调用(包括构造函数调用)的重载解析而言,它更适合单个size_t值; 请参阅函数调用中的单元素向量初始化(尤其是最上面的答案)。
| 归档时间: |
|
| 查看次数: |
853 次 |
| 最近记录: |