使用显式构造函数
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,删除的重载将不会被考虑用于非浮点类型。
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,删除的重载将不会被考虑用于非浮点类型。