使用显式构造函数

Using explicit constructor

class foo {
public:
    explicit foo(int) { std::cout<<"int constructor"; }
}; 

int main() {
    foo f(0.1);
    return 0;
}

我以为使用了 explicit 关键字来防止不需要的类型转换,但上面的代码仍然有效并输出 "int constructor"。为什么?在这种情况下如何防止这种情况发生?

关键字 explicit 强制执行编译时错误,消息为 conversion from ‘int’ to non-scalar type ‘foo’ requested,当您尝试进行这样的隐式转换时:foo f = 1;。这就是它应该做的。

为什么允许浮点值 0.1 已回答

此外,如果您想防止这种行为,请使用这行代码:

foo(double) = delete;

然后你会得到一个错误:use of deleted function ‘foo::foo(double)’ 传递一个 float/double 值。

显式构造函数可防止从构造函数采用的类型隐式转换为您的类型。所以在这种情况下,它阻止了从 int 到 foo 的隐式转换。所以:

foo f = 1;

不会编译。但是,这是显式构造:

foo f(1);

这当然行得通。在你的例子中,你传递了一个双精度而不是一个整数。但是语言中内置了从 double 到 int 的隐式转换,这就是它编译的原因。换句话说,你的问题是这个编译:

int x = 0.1;

然而,在 gcc 上用 -Wconversion 编译(我相信 clang)会对此发出警告,如果你用 -Werror 编译(你应该这样做),它会把这个进入编译错误。如果您使用该编译器,我怀疑 MSVC 也有类似的错误。

你在这里混淆了一些概念。

explicit 防止 隐式构造函数调用 ,而不是 带有隐式参数类型转换的构造函数调用 .

不幸的是,解决方案并不简单。它对新手不太友好,可能需要谷歌搜索才能理解。

可以 显式删除浮点类型的构造函数:foo(float) = delete; 等等。然后 foo f(0.1); 将导致 'use of deleted function' 错误。 但是你也做不到foo f(1l),编译器会抱怨'ambiguous overloads'。

正确的方法如下:

class foo
{
  public:
    foo(int) {std::cout << "int constructor\n";}
    template <typename T,
              typename = std::enable_if_t<std::is_floating_point_v<T>>>
    foo(T) = delete;
}; 

这类似于删除每个浮点类型的重载(如上所述),但由于 SFINAE,删除的重载将不会被考虑用于非浮点类型。