使用std :: initializer_list时防止缩小转换

Red*_*III 20 c++ initializer-list c++11

#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]
}
Run Code Online (Sandbox Code Playgroud)

有没有其他方法可以std::initializer_list从过载列表中删除(即,使非列表ctors更有利),而不是使用()初始化,或者至少禁止缩小转换(除了将警告变为错误)?

我使用的是使用GCC 4.8的http://coliru.stacked-crooked.com/编译器.

And*_*owl 19

实际上,在括号列表初始化器中包含缩小转换的程序是不正确的.我不确定为什么编译器只是给你一个警告,但它肯定应该在这里发出一个错误(FWIW,Clang这样做).

另请注意,这也是一种缩小(因而非法)的转换:

int x { 1.0f }; // ERROR! Narrowing conversion required
Run Code Online (Sandbox Code Playgroud)

根据C++ 11标准的第8.5.4/3段:

列表初始化对象或类型T的引用定义如下:

- 如果T是聚合,则执行聚合初始化(8.5.1).[...]

- 否则,如果初始化列表没有元素[...]

- 否则,如果T是专业化std::initializer_list<E>,[...]

- 否则,如果T是类类型,则考虑构造函数.枚举适用的构造函数,并通过重载决策(13.3,13.3.1.7)选择最佳构造函数.如果转换任何参数需要缩小转换(见下文),则程序格式错误.[...]

更确切地说,标准只说在这种情况下需要"诊断",并且警告是诊断,因此编译器的行为是符合的 - 但我相信发出错误将是更好的行为.

  • 瑞克是对的:标准不允许形成错误的代码,不良形式意味着形式错误.我们经常用于GCC在这里做的术语是GCC具有*符合的扩展* - 它根据需要发出诊断,然后继续为ISO C++中没有法律意义的程序赋予意义. (13认同)
  • @AndyProwl,在缩小转换之前使用`g ++ -std = c ++ 0x`构建现有代码被降级为警告,导致之前格式正确(有时可证明是正确的![示例](http://coliru.stacked-crooked.com/view?id = 5ccdde12b17339c8a9a8a924381b5503-e54ee7a04e4b807da0930236d4cc94dc))代码突然停止编译.缩小转换率导致大多数C++ 11移植问题至少为我所知道的两家大公司,而我在较小代码库中的经验也是如此.如果你想要一个错误,请使用`-pedantic-errors`或`-Werror = narrowing` (6认同)
  • 我现在看到标准允许不正确的代码,只要它提供诊断,但它明确地将这种行为标记为语言的扩展,因此,允许缩小这里是最不可移植的,并且它可能只是一个GCC默认情况下将此错误视为时间问题.在平均时间内,强制出错是你在OP案例中可以做的最合理的事情. (2认同)