简体   繁体   English

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

[英]Use of (templated) deleted function overload to prevent usual arithmetic conversions

I often find that I would like to prevent narrowing or sign conversions (and in general the Usual arithmetic conversions ) for specific constructors or functions. 我经常发现我想防止特定构造函数或函数的缩小或符号转换(通常是通常的算术转换 )。 I tend to write: 我倾向于写:

#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;

This does the job... 这样就可以了...

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
}

Now, is it just a matter of taste in these cases if I use a deleted template or a deleted non-template function, or are there cases where it matters? 现在,如果我使用已删除的模板或已删除的非模板功能,在这些情况下只是一个口味问题,还是在某些情况下很重要? I find the deleted template more explicit, in preventing all T , but on the other hand, when using this pattern with constructors; 我发现在防止所有T的情况下 ,删除的模板更为明确,但是,另一方面,当将此模式与构造函数一起使用时; forwarding constructors have their issues . 转发构造器有他们的问题 In case of the templated version, would it be better to make the deleted template const T& instead? 如果是模板版本,最好将删除的模板设为const T&

First, I think it's worth noting that the non-template versions prevent most cases because they cause an ambiguity between the two overloads, while the template ones do it by providing a better match than the non-template overload. 首先,我认为值得注意的是,非模板版本可防止大多数情况,因为它们会引起两个重载之间的歧义,而模板版本通过提供比非模板重载更好的匹配来做到这一点。 Because of this, the error messages generated by the template versions will tend to be clearer, along the lines of "you've tried to call this deleted function", as opposed to "I can't decide between these two, which one do you actually want?". 因此,由模板版本生成的错误消息趋向于更清晰,类似于“您试图调用此已删除的函数”,而不是“我无法在这两个函数之间做出决定,你真的想要吗?”。 From this point of view, the template version looks better. 从这个角度来看,模板版本看起来更好。

However, there are cases where things behave differently. 但是,在某些情况下,行为会有所不同。

One obscure case is something like foo({f}); 一种晦涩的情况是类似foo({f}); , which is not prevented by the template version, because the initializer list makes the parameter a non-deduced context, so deduction fails for the template, leaving only the non-template overload. ,模板版本没有阻止,因为初始化程序列表使参数成为非推导上下文,所以模板的推导失败,仅留下非模板重载。

For a similar reason, the template version will block foo({i}); 由于类似的原因,模板版本将阻止foo({i}); but won't block foo({3}); 但不会阻止foo({3}); ( 3 is a constant, so the conversion to double is not a narrowing conversion, because 3 fits in a double and generates the same value when converted back). 3是一个常数,所以转换为double并不是缩小转换,因为3拟合为double并在转换回时产生相同的值)。 The non-template version blocks both, because they're both ambiguous. 非模板版本会阻止这两者,因为它们都是模棱两可的。

Another case: 另一种情况:

enum E : unsigned { };

int main() 
{
    E e{};
    bar(e);
}

The template version prevents this, by providing the best overload. 模板版本通过提供最佳的重载来防止这种情况。 The non-template one doesn't, because E to unsigned int is a promotion, which is better than E to int , which is a conversion. 非模板的则不是,因为Eunsigned int是一种晋升,优于Eint是转换。

Similar issues would appear on platforms where, for example, short is the same size as int , and also for conversions from char16_t , char32_t , or wchar_t , depending on their platform-specific representations. 类似的问题也会出现在平台上,例如, shortint大小相同,并且对于char16_tchar32_twchar_t转换也取决于它们特定于平台的表示形式。

Although probably less interesting for the context of the question, another difference appears for: 尽管对于该问题的上下文可能不太有趣,但另一个区别出现在:

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

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

The template version prevents this, again by providing the best overload, while the non-template one doesn't (calls foo(double) ). 模板版本通过提供最佳的重载来防止这种情况,而非模板版本则没有(调用foo(double) )。

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

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