Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/152.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 使用(模板化的)已删除函数重载来防止通常的算术转换_C++_Implicit Conversion - Fatal编程技术网

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

C++ 使用(模板化的)已删除函数重载来防止通常的算术转换,c++,implicit-conversion,C++,Implicit Conversion,我经常发现我希望防止特定构造函数或函数的收缩或符号转换(通常是)。我倾向于写: #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; v

我经常发现我希望防止特定构造函数或函数的收缩或符号转换(通常是)。我倾向于写:

#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;
现在,在这些情况下,如果我使用删除的模板或删除的非模板函数,这仅仅是一个品味问题,还是在某些情况下它很重要?我发现删除的模板更明确,可以防止所有的T,但另一方面,在构造函数中使用此模式时;转发构造函数。
对于模板化版本,是否最好将已删除的模板改为常量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\u t
char32\u t
wchar\u t
转换的平台,具体取决于它们的平台表示

虽然问题的上下文可能不太有趣,但另一个不同之处在于:

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

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

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

或者干脆
t
template void foo(t)=delete;
)@Jarod42。对…一个模糊的情况,事情表现不同,类似于
foo({f}),模板版本不阻止此操作,因为初始值设定项列表使参数成为非推断上下文,因此模板的推断失败,只留下非模板重载。(我无法打开问题中的链接,我不知道是否有提及。)@bogdan,这正是我希望在这些问题中找到的例子。使用init列表的隐式转换非常可怕。然后可以添加
模板void foo(std::initializer_list)=delete
我认为在许多用例中不捕捉
foo({f})
是可以的,因为大括号已经阻止了变窄。@T.C.我同意,但OP想要阻止
foo(f),因此不阻止带括号的版本感觉不一致。我主要是想找出两种方法产生不同结果的情况。我在避免做出阻止或允许的决定——这对我来说太难了。@T.C.,我不明白。f=1.5f和foo({f})正在将1.5f转换为1U@JohanLundberg GCC对
foo({f})
发出警告,对
foo({1.5f)
发出错误。Clang和MSVC对这两个问题都发出错误。
struct A
{
   operator double() { return 7.0; }
};

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