为什么只有在我用大括号初始化一个对象时才需要从 int 到 float 的缩小转换? [重复]

问题描述 投票:0回答:2

我遇到了一件我认为很奇怪的事情:

#include <vector>

int numqueues = 1;
std::vector<float> priorities{numqueues, 1.f };
//^^^ warning: narrowing conversion of numqueues from int to float

//std::vector<float> priorities(numqueues, 1.f );
//^^^ No warning or error. And it's not because it's parsed as a function declaration
// as I can call push_back in main.

int main()
{
    priorities.push_back(1);// No narrowing conversion needed
}

我已经使用几个编译器尝试过,但无法编译。

编辑:据说初始化器列表优先,看起来确实如此,但我试图模仿 std::vector 并且在这个例子中我没有得到缩小转换错误:

#include <vector>
#include <iostream>
#include <initializer_list>

template <typename T>
class MyVector
{public:
    MyVector(size_t s, float f) {
        std::cout << "Called constructor\n";
    }
    MyVector(std::initializer_list<T> init)
    {
        std::cout << "Called initializer list constructor\n";
    }

};

int main()
{

    MyVector<float> foo{ size_t(3), 2.f };
}

我做了完全相同的事情,用 size_t 和 float 初始化它,就像在另一个示例中一样,这个编译得很好。

c++ class constructor initializer-list type-narrowing
2个回答
3
投票

在此声明中

std::vector<float> priorities{numqueues, 1.f };

编译器使用初始化列表构造函数。

vector(initializer_list<T>, const Allocator& = Allocator());

禁止初始化列表的缩小转换。

在此声明中

std::vector<float> priorities(numqueues, 1.f );

编译器使用指定元素数量及其初始值设定项的构造函数。

vector(size_type n, const T& value, const Allocator& = Allocator());

来自 C++ 14 标准(8.5.4 列表初始化)

2 如果构造函数的第一个构造函数是初始化列表构造函数 参数的类型为 std::initializer_list 或可能的引用 对于某些类型 E,cv 限定的 std::initializer_list ,以及 没有其他参数或者所有其他参数都有 默认参数(8.3.6)。 [ 注意:初始化列表构造函数是 在列表初始化中比其他构造函数更受青睐

和(13.3.1.7 通过列表初始化进行初始化)

1 当非聚合类类型 T 的对象被列表初始化时,例如 8.5.4 指定重载决策是根据 根据本节中的规则,重载决策选择 分两个阶段的构造函数:

(1.1) — 最初,候选函数是初始化列表 类 T 的构造函数 (8.5.4) 和参数列表包括 初始化列表作为单个参数。

(1.2) — 如果没有找到可行的初始化列表构造函数,则重载 再次进行解析,其中候选函数全部为 类 T 的构造函数和参数列表由以下组成 初始化列表的元素。

这是一个演示程序

#include <iostream>
#include <initializer_list>

struct A
{
    A( std::initializer_list<float> )
    {
        std::cout << "A( std::initializer_list<float> )\n";
    }
    
    A( size_t, float )
    {
        std::cout << "A( size_t, float )\n";
    }
};

int main() 
{
    A a1 { 1, 1.0f };
    A a2( 1, 1.0f );
    
    return 0;
}

程序输出为

A( std::initializer_list<float> )
A( size_t, float )

至于你附加的问题(8.5.4列表初始化)

7 缩小转换是隐式转换

(7.3) — 从整数类型或无作用域枚举类型到 浮点类型,除非源是常量 表达式和转换后的实际值将适合 目标类型,转换回时将产生原始值 原始类型,或

所以在这个列表中初始化

MyVector<float> foo{ size_t(3), 2.f };

使用适合 float 类型的常量表达式

size_t( 3 )

例如,如果在上面的演示程序中您将编写

size_t n = 1;

A a1{ n, 1.0f };

然后编译器应该发出有关缩小转换的消息(至少 MS VS 2019 C++ 编译器发出这样的错误消息)。


1
投票

来自列表初始化的cppreference

列表初始化通过以下方式限制允许的隐式转换 禁止以下行为:

  • ...

  • 从整数类型到浮点类型的转换,除非源是可以存储其值的常量表达式 完全符合目标类型

通常,列表初始化不会为您执行隐式转换。另外,因为

std::vector
有一个构造函数
vector( std::initializer_list<T> init, const Allocator& alloc = Allocator() );
,所以它是被调用来构造
vector
的构造函数。

© www.soinside.com 2019 - 2024. All rights reserved.