为什么编译器会在编译错误时将不正确的参数types传递给struct initialiser list?
我定义了一个结构体,它有一个构造函数:
struct MyStruct { MyStruct(const int value) : value(value) { } int value; };
和以下对象:
int main() { MyStruct a (true); MyStruct b {true}; }
但是我没有收到任何编译错误,无论是MVS2015或Xcode 7.3.1。
- 为什么我没有收到任何编译错误?
- 我如何让编译器帮我检测到这个? (最初,结构被写成具有
bool
数据,但一段时间后,代码改变了,bool
变成了int
,引入了一些错误。)
一个bool
可以隐式转换为一个int
值的方式保存。 使用大括号初始化的唯一不允许的转换是缩小转换 (例如反转bool{42}
)。
如果你想确保你的类只能用int
来构造,那么直接的方法就是delete
所有其他的构造函数:
struct MyStruct { explicit MyStruct(int i) : value(i) { } template <typename T> MyStruct(T t) = delete; int value; };
在这里, MyStruct{true}
和MyStruct(false)
将调用MyStruct::MyStruct<bool>
,它被定义为删除,因此是不合格的。
这个优于static_assert
是,所有的types特征实际上会产生正确的值。 例如, std::is_constructible<MyStruct, bool>
是std::false_type
。
这是一个允许你只从int
值初始化你的类的构造:
#include <type_traits> struct MyStruct { template <typename T> MyStruct(T t) : value(t) { static_assert(std::is_same<T, int>::value, "Bad!"); } int value; };
这是因为此构造函数模板所需的模板参数推导将产生参数的确切types而不执行转换,因此您可以对该types执行testing。
你也许应该也可以使用SFINAE来约束构造函数,这样MyStruct
就不会出现任何构造。
此外,你应该也可以使构造函数模板explicit
使随机整数不成为MyStruct
实例。
换句话说,我会这样写:
struct MyStruct { template <typename T, typename = std::enable_if_t<std::is_same<T, int>::value>> MyStruct(T t) : value(t) {} // ...
最简单的解决scheme是声明一个布尔构造函数为删除不是吗?
struct MyStruct { MyStruct(bool) = delete; MyStruct(const int value) : value(value) { } int value; };
示例错误输出:
... /Users/xxxxxxx/play/fast_return/skeleton/main.cpp:68:14: error: call to deleted constructor of 'MyStruct' MyStruct b {true}; ^ ~~~~~~ /Users/xxxxxxx/play/fast_return/skeleton/main.cpp:57:9: note: 'MyStruct' has been explicitly marked deleted here MyStruct(bool) = delete; ^ 2 errors generated.
因为一个bool
可以隐式转换为一个int
。
这是一个语言function,你不能closures,对不起。
值true被转换为值1(int)。