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

时间:2013-06-05 12:04:00

标签: c++ c++11 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/编译器。

4 个答案:

答案 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)。 [...]

     

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

     

- 否则,如果Tstd::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)

您可以使用-Wno-c++11-narrowing关闭错误:

以下是一个示例测试程序:

#include <cstdint>

struct foo {
    int32_t a;
};

void foo(int64_t val) {
    struct foo A = { val };
}

使用只有-std=c++11的clang ++ - 3.8编译,我们得到了所声明的错误:

enter image description here

添加-Wno-c++11-narrowing,金色沉默: - )

enter image description here

当然,缩小的问题可能会在稍后再次让你感到困惑,但有时可能更容易将技术债务的痛苦推迟到以后。 ymmv: - )