c++ - 使用(模板化的)删除函数重载来防止通常的算术转换

标签 c++ implicit-conversion

我经常发现我想阻止特定构造函数或函数的缩小或符号转换(通常是 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
}

现在,在这些情况下,如果我使用已删除的模板或已删除的非模板函数,这只是一个品味问题,还是在某些情况下很重要?我发现删除的模板更明确,可以防止 all T,但另一方面,当将此模式与构造函数一起使用时;转发构造函数have their issues . 如果是模板化版本,将删除的模板设为 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_t 的转换, char32_twchar_t,具体取决于它们特定于平台的表示形式。

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

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

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

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

关于c++ - 使用(模板化的)删除函数重载来防止通常的算术转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41848592/

相关文章:

java - jsf - 字符串无法解析为数字

c++ - 为什么 C++ 允许我将 const char 分配给 const char *?!

scala - Scala 中的 ISO 宏

c++ - C++获取APPDATA的路径

c++ - 将拜耳图像分离到颜色 channel C++

c++ - 在不传递参数的情况下重载 C++ 函数

c++ - 为什么可以将整数值赋给未初始化的指针

java - 当将 Boolean 转换为 boolean 时 Eclipse 可以给我警告吗

c - 通过将数组作为参数传递给函数来增加数组的大小

c++ - 从成员函数创建线程时出现段错误(核心转储)错误