繁体   English   中英

使用(模板化)已删除的函数重载来防止常规的算术转换

[英]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);
}

模板版本通过提供最佳的重载来防止这种情况。 非模板的则不是,因为Eunsigned int是一种晋升,优于Eint是转换。

类似的问题也会出现在平台上,例如, shortint大小相同,并且对于char16_tchar32_twchar_t转换也取决于它们特定于平台的表示形式。

尽管对于该问题的上下文可能不太有趣,但另一个区别出现在:

struct A
{
   operator double() { return 7.0; }
};

int main() 
{
   A a{};
   foo(a);
}

模板版本通过提供最佳的重载来防止这种情况,而非模板版本则没有(调用foo(double) )。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM