调用构造函数时必须有多明确

How explicit must you be when calling a constructor?

本文关键字:构造函数 调用      更新时间:2023-10-16

我有这个类

struct foo
{
    explicit foo(const std::uint32_t& x, const std::uint32_t& y);
};

以及方法

int main()
{
    std::int32_t x = -1;
    std::int32_t y = -1;
    foo f(x, y);
}

在我的编译器(MSVC2012)上,它编译并运行时将值xy包装为无符号类型。我没想到会出现这种情况,但由于类型不匹配,我预计会出现编译错误。

我错过了什么?

如果运气不好,标准确实允许通过为常量引用传递的参数创建匿名临时参数来隐式转换已签名到未签名。

(请注意,对于非常量引用,这是不正确的)。

如果您使用的是C++11,最好的方法是使用删除构造函数

foo(const std::int32_t& x, const std::int32_t& y) = delete;

在C++11之前,你可以让这个构造函数private,而不是定义。就像老式的不可复制的习惯用法一样。

MSVC2012是一种中途之家C++03/C++11编译器。它实现了一些C++11特性,但没有实现其他特性。不幸的是,构造函数的删除是不支持的功能之一,因此private方法是可用的最佳方法。

实际上,您应该使用新的大括号初始化语法foo f{x, y},它至少会发出警告。之后,您可以将编译器配置为将警告视为错误并进行相应处理,因为好的代码通常也应该消除警告(因为如果希望进行转换,则应该使用显式强制转换)。

explicit不会阻止使用构造函数参数的隐式转换(在绑定引用时会明显发生这种转换);它防止了隐式构造。

void bar(foo);
int main()
{
   foo f({0, 0}); // doesn't matter that the arguments are implicitly converted
   bar({0, 0});   // error - implicit conversion required here to pass a foo
   bar(f);        // crucially, ok because argument requires no conv. construction
}