[英]Use of (templated) deleted function overload to prevent usual arithmetic conversions
我经常发现我想防止特定构造函数或函数的缩小或符号转换(通常是通常的算术转换 )。 我倾向于写:
#include <iostream>
void foo(double f){
std::cout << "foo double" << f <<std::endl;
}
void foo(float) = delete;
// or template<typename T> void foo(T&& f) = delete;
void bar(unsigned int f){
std::cout << "bar uint " << f <<std::endl;
}
void bar(signed int ) = delete;
// or template<typename T> void bar(T&& f) = delete;
这样就可以了...
int main() {
auto i=2;
auto d=2.0;
auto f=2.0f;
foo(i); // prevented
foo(d); // OK
foo(f); // prevented
auto uil = 3ull;
auto ul = 3ul;
auto u = 3u;
bar(i); // prevented
bar(d); // prevented
bar(f); // prevented
bar(uil); // prevented
bar(ul); // prevented
bar(u); // OK
}
现在,如果我使用已删除的模板或已删除的非模板功能,在这些情况下只是一个口味问题,还是在某些情况下很重要? 我发现在防止所有T的情况下 ,删除的模板更为明确,但是,另一方面,当将此模式与构造函数一起使用时; 转发构造器有他们的问题 。 如果是模板版本,最好将删除的模板设为const T&
?
首先,我认为值得注意的是,非模板版本可防止大多数情况,因为它们会引起两个重载之间的歧义,而模板版本通过提供比非模板重载更好的匹配来做到这一点。 因此,由模板版本生成的错误消息趋向于更清晰,类似于“您试图调用此已删除的函数”,而不是“我无法在这两个函数之间做出决定,你真的想要吗?”。 从这个角度来看,模板版本看起来更好。
但是,在某些情况下,行为会有所不同。
一种晦涩的情况是类似foo({f});
,模板版本没有阻止,因为初始化程序列表使参数成为非推导上下文,所以模板的推导失败,仅留下非模板重载。
由于类似的原因,模板版本将阻止foo({i});
但不会阻止foo({3});
( 3
是一个常数,所以转换为double
并不是缩小转换,因为3
拟合为double
并在转换回时产生相同的值)。 非模板版本会阻止这两者,因为它们都是模棱两可的。
另一种情况:
enum E : unsigned { };
int main()
{
E e{};
bar(e);
}
模板版本通过提供最佳的重载来防止这种情况。 非模板的则不是,因为E
到unsigned int
是一种晋升,优于E
到int
是转换。
类似的问题也会出现在平台上,例如, short
与int
大小相同,并且对于char16_t
, char32_t
或wchar_t
转换也取决于它们特定于平台的表示形式。
尽管对于该问题的上下文可能不太有趣,但另一个区别出现在:
struct A
{
operator double() { return 7.0; }
};
int main()
{
A a{};
foo(a);
}
模板版本通过提供最佳的重载来防止这种情况,而非模板版本则没有(调用foo(double)
)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.