#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/编译器。
答案 0 :(得分:19)
实际上,在括号列表初始化程序中包含缩小转换的程序格式错误。我不确定为什么编译器会给你一个警告,但它肯定会在这里发出错误(FWIW,Clang does that)。
另请注意,这也是一种缩小(因而非法)的转换:
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)。 如果缩小转换(请参阅 如果需要转换任何参数,程序就是格式错误。 [...]
更确切地说,标准只说在这种情况下需要“诊断”,并且警告是诊断,因此编译器的行为是符合的 - 但我相信发出错误将是一种更好的行为。 / p>
答案 1 :(得分:3)
这看起来像编译器错误。您应该收到错误而不是警告。支撑初始化永远不应该隐式缩小。
从标准(第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
答案 2 :(得分:2)
您可以使用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
答案 3 :(得分:0)