[英]Preventing narrowing conversion when using std::initializer_list
#include <iostream>
struct X {
X(std::initializer_list<int> list) { std::cout << "list" << std::endl; }
X(float f) { std::cout << "float" << std::endl; }
};
int main() {
int x { 1.0f };
X a(1); // float (implicit conversion)
X b{1}; // list
X c(1.0f); // float
X d{1.0f}; // list (narrowing conversion) ARG!!!
// warning: narrowing conversion of '1.0e+0f' from 'float' to 'int'
// inside { } [-Wnarrowing]
}
有没有其他方法可以从重载列表中删除std::initializer_list
(即,使非列表ctors更有利),而不是使用()-initialization,或者至少禁止缩小转换(除了将警告转换为错误)?
我使用的是使用GCC 4.8的http://coliru.stacked-crooked.com/编译器。
实际上,在括号列表初始化器中包含缩小转换的程序是不正确的 。 我不确定为什么编译器只是给你一个警告,但它肯定应该在这里发出一个错误(FWIW, Clang这样做 )。
另请注意,这也是一种缩小(因而非法)的转换:
int x { 1.0f }; // ERROR! Narrowing conversion required
根据C ++ 11标准的第8.5.4 / 3段:
列表初始化对象或类型T的引用定义如下:
- 如果
T
是聚合,则执行聚合初始化(8.5.1)。 [...]- 否则,如果初始化列表没有元素[...]
- 否则,如果
T
是std::initializer_list<E>
,[...]- 否则,如果
T
是类类型,则考虑构造函数。 枚举适用的构造函数,并通过重载决策(13.3,13.3.1.7)选择最佳构造函数。 如果转换任何参数需要缩小转换(见下文),则程序格式错误 。 [...]
更确切地说,标准只说在这种情况下需要“诊断”,并且警告是诊断,因此编译器的行为是符合的 - 但我相信发出错误将是更好的行为。
这看起来像编译器错误。 您应该收到错误而不是警告。 大括号初始化永远不应该隐含地缩小。
从标准(第8.5.4节)
struct B { B(std::initializer_list<int>); }; B b1 { 1, 2 }; // creates initializer_list<int> and calls constructor B b2 { 1, 2.0 }; // error: narrowing
你可以用std::enable_if
实现你想要的。
#include <iostream>
#include <type_traits>
struct X {
template<typename T, typename = typename std::enable_if<std::is_same<T,int>::value>::type>
X(std::initializer_list<T>) { std::cout << "list" << std::endl; }
X(float) { std::cout << "float" << std::endl; }
};
int main() {
X a(1); // float (implicit conversion)
X b{1}; // list
X c(1.0f); // float
X d{1.0f}; // float (yay)
}
适用于g ++ 4.8和clang 3.2
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.