列表初始化({...}
语法)不允许缩小转换.例如,尝试列表初始化一个int i
with会3.14f
产生编译错误,因为从浮点值到整数的转换正在缩小:
<source>:11:32: error: narrowing conversion of '3.1400001e+0f' from 'float' to 'int' inside { } [-Wnarrowing]
int i{3.14f};
^
Run Code Online (Sandbox Code Playgroud)
随着中说,为什么它可以构造一个float f
与3.14
,这是类型的double
?(从转换double
到float
被认为是缩小的.)执行以下操作:
float f{3.14};
Run Code Online (Sandbox Code Playgroud)
不保留编译错误.
看看这个简单的程序:
int main() {
float f2 = 7.2; // OK, with warning
float f3 = 7.199999809265137; // OK, no warning
float f4{ 7.2 }; // Fails
float f5{ 7.199999809265137 }; // OK, no warning
float f6 = { 7.2 }; // Fails
float f7 = { 7.199999809265137 }; // OK, no warning
}
Run Code Online (Sandbox Code Playgroud)
使用默认选项(cl /W4
版本19.00.23918)使用MSVC 2015编译时,我收到以下消息:
FloatTest.cpp(2): warning C4305: 'initializing': truncation from 'double' to 'float'
FloatTest.cpp(4): error C2397: conversion from 'double' to 'float' requires a narrowing conversion
FloatTest.cpp(4): …
Run Code Online (Sandbox Code Playgroud) 在这个答案的背景下,问题就出现了.
考虑一个例子:
struct foo {
int value;
operator int&(){ return value; }
operator int(){ return value; }
};
int main () {
int &a(foo{}); // #1
//int &b{foo{}}; // #2 -- ambiguity
int &c = foo{}; // #3
//int &d = {foo{}}; // #4-- ambiguity
int &d { a }; // #5
int &e = { a }; // #6
(void)a;
(void)c;
(void)d;
(void)e;
}
Run Code Online (Sandbox Code Playgroud)
我不明白为什么#2和#4引起歧义,而#1和#3则不然.所以问题是 - 为什么直接列表初始化会导致隐式强制转换引用的歧义,如果声明类型和对类型的引用的转换运算符?
在C++ 11中,我有以下联合:
union SomeData
{
std::uint8_t Byte;
std::uint16_t Word;
std::uint32_t DWord;
unsigned char String[128];
};
Run Code Online (Sandbox Code Playgroud)
如果我这样初始化联盟;
SomeData data {};
Run Code Online (Sandbox Code Playgroud)
是否保证联盟的全部内容都将"归零"?换一种方式; 是一个联合的空列表初始化程序,在功能上等同于将联合设置为零?:
memset(&data, 0, sizeof(data));
Run Code Online (Sandbox Code Playgroud)
特别是,我关心字符串数据.我想确保字符串的整个长度包含零.它似乎在我当前的编译器中工作,但规范的语言是否保证这始终是真的?
如果不是:有没有更好的方法将联合的全长初始化为零?
以下代码成功编译了大多数现代C++ 11兼容编译器(GCC> = 5.x,Clang,ICC,MSVC).
#include <string>
struct A
{
explicit A(const char *) {}
A(std::string) {}
};
struct B
{
B(A) {}
B(B &) = delete;
};
int main( void )
{
B b1({{{"test"}}});
}
Run Code Online (Sandbox Code Playgroud)
但是为什么它首先编译,以及列出的编译器如何解释该代码?
为什么MSVC能够在没有的情况下编译它B(B &) = delete;
,但其他3个编译器都需要它?
为什么在删除复制构造函数的不同签名时,除了MSVC之外的所有编译器都会失败,例如B(const B &) = delete;
?
编译器甚至都选择相同的构造函数吗?
为什么Clang会发出以下警告?
17 : <source>:17:16: warning: braces around scalar initializer [-Wbraced-scalar-init]
B b1({{{"test"}}});
Run Code Online (Sandbox Code Playgroud) 在C++ 11之前,我们可以通过编写类似于A a = 1;
或多或少相当的东西来进行复制初始化A a = A(1);
.也就是说,首先创建临时,然后调用复制ctor.无论版本是否复制,这必须是概念上的,并且必须可以访问复制文件.
使用C++ 11中的列表初始化,我们可以通过写入来进行复制列表初始化A a = {1, 2};
.在我看来,这应该或多或少相当于A a = A(1, 2);
.但是,在GCC和clang上,A a = {1, 2}
即使复制和移动ctor不可访问(通过声明为私有)也会编译.但是,A a = 1;
如果相应的复制/移动ctor不可访问,则不会在GCC或clang上编译.所以,A a = {1, 2};
似乎或多或少等同于A a{1, 2};
直接列表初始化.这与实际直接列表初始化之间的区别在于,A a = {1, 2};
如果采用两个整数的ctor是显式的,则不会编译.在这方面,A a = {1, 2};
类似于复制初始化.
所以,我的问题是:A a = {1, 2};
概念上表达式的确切语义是什么?从概念上讲,复制省略不会妨碍.
c++ copy-constructor copy-initialization c++11 list-initialization
我正在尝试编写一个基于数学向量的类:
template <unsigned N> class Vector{
public:
Vector() = default;
Vector(std::initializer_list<double> li) { *this = li;}
Vector& operator=(std::initializer_list<double>);
private:
std::array<double, N> x = {}
}
template <unsigned N> inline Vector<N>& Vector<N>::operator=(std::initializer_list<double> li){
if(N != li.size()) throw std::length_error("Attempt to initialise Vector with an initializer_list of different size.");
std::copy(li.begin(), li.end(), x.begin());
return *this;
}
Run Code Online (Sandbox Code Playgroud)
我希望能够编写这样的代码;
Vector<3> a = {1,2,3};
a = {3,5,1};
Run Code Online (Sandbox Code Playgroud)
用户期望编写这样的代码是很自然的,对吧?但是,如果我使用错误大小的初始化列表,我希望发生编译时错误std::array
.
std::array<double, 3> a = {2,4,2,4} //compile time error
Vector<3> a = {3,5,1,5} //run-time error as …
Run Code Online (Sandbox Code Playgroud) 我正在为此提交GCC错误,但我宁愿仔细检查一下.
考虑以下程序:
#include <utility>
template<typename T, typename A>
void F(A&& a) { T(std::forward<A>(a)); } // Note: () syntax.
int main() { int i; F<int&>(i); }
Run Code Online (Sandbox Code Playgroud)
和:
#include <utility>
template<typename T, typename A>
void F(A&& a) { T{std::forward<A>(a)}; } // Note: {} syntax.
int main() { int i; F<int&>(i); }
Run Code Online (Sandbox Code Playgroud)
最新的Clang和MSVC编译器都接受这两个程序.GCC 5及以后接受第一个程序,但拒绝第二个程序,声称invalid cast of an rvalue expression of type 'int' to type 'int&'
.
这是GCC的错误吗?还是这的确之间的差异T{}
,并T()
在上述背景下(因此臭虫锵和MSVC)?
编辑:
问题可以缩小到以下简单的摘录:
int i; (int&){i};
Run Code Online (Sandbox Code Playgroud)
和
int i; (int&)(i);
Run Code Online (Sandbox Code Playgroud) DIRECT-VS COPY-INITIALIZATION
通过这个问题(它是直接初始化还是复制初始化?)我学到了直接初始化和复制初始化之间的区别:
Run Code Online (Sandbox Code Playgroud)direct-initialization copy-initialization ----------------------- --------------------- obj s("value"); obj s = obj("value"); obj s = "value"; obj s{"value"}; obj s = {"value"}; obj s = obj{"value"};
为了完整起见,我在这里提到它.我对此页面的实际问题列在下一段>>
直接初始化与直接列表初始化
答案显示,在直接初始化的范畴内,可以在直接初始化和直接列表初始化之间产生差异.
Run Code Online (Sandbox Code Playgroud)obj s("value"); // direct-initialization obj s{"value"}; // direct-list-initialization
我知道列表初始化不允许缩小,这样初始化int x{3.5};
就不会编译.但除此之外,我还有几个问题:
(1)
obj s("value");
和之间的编译器输出有什么不同obj s{"value"};
吗?
让我们考虑一个没有任何优化的编译器.我想知道任何可能的技术差异:-)
(2)也许我应该问一个多变量初始化完全相同的问题,例如:
obj s("val1", "val2");
和obj s{"val1", "val2"};
(3)我注意到列表初始化有时可以调用不同的构造函数,如:
vector<int> a{10,20}; //Curly braces -> fills the …
Run Code Online (Sandbox Code Playgroud) 这两行来自cppreference
这两种说法有什么区别?我看不出任何区别
直到 C++14
如果花括号初始化列表为空并且 T 是具有默认构造函数的类类型,则执行值初始化。否则,如果 T 是聚合类型,则执行聚合初始化。
从 C++14 开始
如果 T 是聚合类型,则执行聚合初始化。否则,如果花括号初始化列表为空并且 T 是具有默认构造函数的类类型,则执行值初始化。