将NULL作为参数传递时,函数重载如何工作?

时间:2018-07-17 23:56:35

标签: c++ c++11 overloading overload-resolution

我知道NULL被定义为0。它似乎是一个int常量,已转换为指针类型。因此,当有两个重载函数时会发生什么:一个重载指针类型,另一个重载int类型。与nullptr相比,该如何工作?

#include <iostream>
using namespace std;

void callMe(int* p)
{
    cout << "Function 1 called" << endl;
}

void callMe(int i)
{
    cout << "Function 2 called" << endl;
}

int main()
{
    callMe(nullptr);
    callMe(NULL);

    return 0;
}

我知道callMe(nullptr)肯定会调用第一个函数。但是callMe(NULL)将调用哪个函数?我在计算机上编译了此代码。只有一个警告。

g++ -std=c++11 nullptr_type.cpp
nullptr_type.cpp: In function 'int main()':
nullptr_type.cpp:44:14: warning: passing NULL to non-pointer argument 1 of 'void callMe(int)' [-Wconversion-null]
   callMe(NULL);
          ^

我的代码编译时没有任何问题,当我运行它时,我看到callMe(NULL)称为Function2。VisualStudio也会编译该代码,并调用Function 2。

但是,当我尝试在在线GeeksForGeeks IDE上编译代码时,会出现一些编译器错误。

https://ide.geeksforgeeks.org/UsLgXRgpAe

我想知道为什么会发生这些错误。

prog.cpp: In function 'int main()':
prog.cpp:17:13: error: call of overloaded 'callMe(NULL)' is ambiguous
  callMe(NULL);
             ^
prog.cpp:4:6: note: candidate: void callMe(int*)
 void callMe(int* p)
      ^
prog.cpp:9:6: note: candidate: void callMe(int)
 void callMe(int i)
      ^

在线Ideone IDE和在线TutorialsPoint CodingGround IDE也会产生相同的错误。这是实际定义的行为吗?

这种情况如何?哪些函数将在这里调用?

#include <iostream>
using namespace std;

void callMe(int* p)
{
    cout << "Function 1 called" << endl;
}

void callMe(char* cp)
{
    cout << "Function 2 called" << endl;
}

void callMe(nullptr_t np)
{
    cout << "Function 3 called" << endl;
}

void callMe(int i)
{
    cout << "Function 4 called" << endl;
}

int main()
{
    callMe(nullptr);
    callMe(NULL);

    return 0;
}

一个更笼统的问题,但我认为这就是要解决的所有问题:您如何知道重载函数时哪个函数调用是模棱两可的?在有多个功能是该论证的竞争者的情况下,此问题。如果只有一个函数,它将采用无错误的参数。但是,当几个函数似乎同样有可能接受该参数时,会发生什么?

链接的问题是关于在C中使用NULL的问题。我的问题是关于C ++的问题。 C编程语言没有运算符重载,因此,我涉及的大多数问题仅是C ++特定的问题。

================================================ ==========================

这是一个相关的问题,详细说明了该问题及其解决方案。在那个答案中有很多有用的答案。

What are the advantages of using nullptr?

2 个答案:

答案 0 :(得分:5)

  

我知道NULL被定义为0。

通常情况并非如此,可以将其定义为nullptr。可能是0L。还有其他可能性,例如我尝试过的gcc版本将其定义为__null,这显然是gcc的空指针常量扩展。

  

如何知道重载函数时哪些函数调用是模棱两可的?

通过阅读C标准的“过载分辨率”部分。这是其中最复杂的部分。简而言之,对不同的转换序列进行排名,并且某些转换序列的排名高于其他转换序列。有一个介绍on cppreference

  • callMe(0)解析为(int),因为完全匹配优先于转化。 (调用(int *)版本需要指针转换的整数)
  • callMe(nullptr)解析为(int *),因为甚至没有从nullptrint的转换
  • callMe(NULL)取决于NULL的定义方式。如果是0nullptr,请参见上文。如果为0L,则模棱两可,因为需要进行转换才能匹配大小写,并且整数转换的等级与整数到指针的转换相同。等等

答案 1 :(得分:2)

  

我知道NULL被#定义为0

这不是标准所保证的。 NULL可以是任何有效的空指针文字,包括00Lnullptr

但是,如果我们假设在标准库实现中实际上将NULL定义为0,那么重载解析的行为将与传递参数时的行为完全一样。文字0

  

似乎是一个int常量,已转换为指针类型。

0实际上既是int 又是指针常量。但是,如果存在冲突,则int是重载解析(以及模板参数检测)的首选。

  

但是callMe(NULL)将调用哪个函数?

是否定义了实现,具体取决于NULL的定义方式。

  

如何知道重载函数时哪些函数调用是模棱两可的?

了解函数调用是否模棱两可的前提是知道用作参数的表达式的类型。由于NULL表达式的类型是实现定义的,因此在某些情况下,实现以不同的方式解决重载-或由于歧义而无法解决。

在您知道表达式类型的情况下,标准规则将准确告诉您何时调用是模棱两可的,以及何时首选一个重载而不是另一个重载。过载规则非常复杂,并且其中有很多。该标准的[over]部分应该是最相关的。

  

[over.match.viable]:从为给定上下文([over.match.funcs])构造的候选函数集中,选择了一组可行的函数,从中将选择最佳函数< / strong>,以比较参数转换序列和相关约束([temp.constr.decl])以获得最佳拟合([over.match.best])。 ...

     

[over.best.ics] ...如果选择使用歧义转换序列的函数作为最佳可行函数,则该调用将格式错误,因为该调用中参数之一的转换是歧义的。 / p>