为什么仅当我用大括号初始化一个对象时才需要从 int 到 float 的缩小转换?
Why is a narrowing conversion from int to float only needed if I brace-initialise an object?
我运行进入我认为很奇怪的事情:
#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
}
我已经用几个编译器试过了,这个编译不了。
编辑:有人说 initializer_list 优先,看起来是这样,但我试图模仿 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 初始化它,就像在另一个例子中一样,这个编译很好。
在此声明中
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 A constructor is an initializer-list constructor if its first
parameter is of type std::initializer_list or reference to possibly
cv-qualified std::initializer_list for some type E, and either
there are no other parameters or else all other parameters have
default arguments (8.3.6). [ Note: Initializer-list constructors are
favored over other constructors in list-initialization
和(13.3.1.7 通过列表初始化进行初始化)
1 When objects of non-aggregate class type T are list-initialized such
that 8.5.4 specifies that overload resolution is performed according
to the rules in this section, overload resolution selects the
constructor in two phases:
(1.1) — Initially, the candidate functions are the initializer-list
constructors (8.5.4) of the class T and the argument list consists of
the initializer list as a single argument.
(1.2) — If no viable initializer-list constructor is found, overload
resolution is performed again, where the candidate functions are all
the constructors of the class T and the argument list consists of the
lements of the initializer list.
这是一个演示程序
#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 List-initialization)
7 A narrowing conversion is an implicit conversion
(7.3) — from an integer type or unscoped enumeration type to a
floating-point type, except where the source is a constant
expression and the actual value after conversion will fit into the
target type and will produce the original value when converted back to
the original type, or
所以在此列表初始化
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++ 编译器会发出这样的错误消息)。
来自 cppreference on list initialisation.
list-initialization limits the allowed implicit conversions by
prohibiting the following:
...
conversion from an integer type to a floating-point type, except where the source is a constant expression whose value can be stored
exactly in the target type
通常,列表初始化不会为您进行隐式转换。此外,因为 std::vector
有一个构造函数 vector( std::initializer_list<T> init, const Allocator& alloc = Allocator() );
,它是被调用来构造 vector
.
的构造函数
我运行进入我认为很奇怪的事情:
#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
}
我已经用几个编译器试过了,这个编译不了。
编辑:有人说 initializer_list 优先,看起来是这样,但我试图模仿 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 初始化它,就像在另一个例子中一样,这个编译很好。
在此声明中
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 A constructor is an initializer-list constructor if its first parameter is of type std::initializer_list or reference to possibly cv-qualified std::initializer_list for some type E, and either there are no other parameters or else all other parameters have default arguments (8.3.6). [ Note: Initializer-list constructors are favored over other constructors in list-initialization
和(13.3.1.7 通过列表初始化进行初始化)
1 When objects of non-aggregate class type T are list-initialized such that 8.5.4 specifies that overload resolution is performed according to the rules in this section, overload resolution selects the constructor in two phases:
(1.1) — Initially, the candidate functions are the initializer-list constructors (8.5.4) of the class T and the argument list consists of the initializer list as a single argument.
(1.2) — If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class T and the argument list consists of the lements of the initializer list.
这是一个演示程序
#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 List-initialization)
7 A narrowing conversion is an implicit conversion
(7.3) — from an integer type or unscoped enumeration type to a floating-point type, except where the source is a constant expression and the actual value after conversion will fit into the target type and will produce the original value when converted back to the original type, or
所以在此列表初始化
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++ 编译器会发出这样的错误消息)。
来自 cppreference on list initialisation.
list-initialization limits the allowed implicit conversions by prohibiting the following:
...
conversion from an integer type to a floating-point type, except where the source is a constant expression whose value can be stored exactly in the target type
通常,列表初始化不会为您进行隐式转换。此外,因为 std::vector
有一个构造函数 vector( std::initializer_list<T> init, const Allocator& alloc = Allocator() );
,它是被调用来构造 vector
.