【问题标题】:Redefined and ambiguous call to functions have array parameter对函数的重新定义和模棱两可的调用具有数组参数
【发布时间】:2020-02-25 17:57:23
【问题描述】:

我想看看数组和数组引用参数之间的区别,我得到了一个重新定义的错误和一个模棱两可的错误。我不明白为什么编译器不能告诉他们:

#include "stdafx.h"
#include <iostream>
#include <string>

using namespace std;

typedef int arrTen[10];

void fun(int arr[]) { 
   cout << "arr[] called" << endl;
}

void fun(arrTen arr) {
   cout << "arrTen called" << endl;
   //cout << end(arr) - begin(arr) << endl;
}

void fun(arrTen &arr) {
   cout << "arrTen reference called" << endl;
   cout << end(arr) - begin(arr) << endl;
}

int main()
{
   int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
   fun(arr); //ambiguous call to overloaded function

   return 0;
}

错误信息:

Demo.cpp
demo.cpp(24): error C2084: function 'void fun(int [])' already has a body
demo.cpp(20): note: see previous definition of 'fun'
demo.cpp(37): error C2668: 'fun': ambiguous call to overloaded function
demo.cpp(29): note: could be 'void fun(arrTen (&))'
demo.cpp(24): note: or       'void fun(int [])'
demo.cpp(37): note: while trying to match the argument list '(int [10])'
  1. 似乎 fun(int arr[])fun(arrTen arr) 被重新定义了。 不知道为什么数组点参数等于数组参数。

  2. 当我注释掉 fun(int arr[]) 时,fun(arr) 是一个模棱两可的电话。 为什么编译器无法判断我已经传递了对fun 的引用?

【问题讨论】:

    标签: c++ arrays parameter-passing


    【解决方案1】:

    当您使用数组语法(使用[])声明函数参数时,编译器会将其视为指针

    void fun(int arr[]) 被解析为void fun(int* arr)

    该数组是typedef创建的类型别名无关紧要,它仍然会被解析为指针。

    这意味着采用int []arrTen 参数的函数是相同的。


    至于歧义,两个函数重载(一个采用指针,另一个采用对arrTen 的引用)同样有效,编译器无法选择调用哪一个。

    【讨论】:

      【解决方案2】:

      这两个函数

      void fun(int arr[]) { 
         cout << "arr[] called" << endl;
      }
      
      void fun(arrTen arr) {
         cout << "arrTen called" << endl;
         //cout << end(arr) - begin(arr) << endl;
      }
      

      具有相同的类型

      void fun( int  *arr);
      

      因为具有数组类型的参数被编译器隐式调整为指向元素类型的指针。

      所以这段代码打破了单一定义规则。

      为了清楚起见,请考虑这些函数声明

      void fun( int arr[100] );
      void fun( int arr[10] );
      void fun( int arr[1] );
      void fun( int arr[] );
      void fun( int *arr );
      

      它们都声明了同一个函数,所有这些声明都可以包含在编译单元中,尽管它们是多余的。但是函数应该只有一个定义。

      另一方面,编译器也不能在通过引用或值接受参数的两个函数之间进行选择。

      你可以用一个常量参数来做一个函数,比如

      void fun( const arrTen arr) {
         cout << "arrTen called" << endl;
         //cout << end(arr) - begin(arr) << endl;
      }
      

      在这种情况下,将调用通过引用接受参数的函数。

      【讨论】:

      • 我得到该数组参数将衰减为数组元素类型指针,但是为什么带有 const 限定符的参数可以接受引用?而end()、begin()函数不能以arr为参数,看来arr已经不是聚合对象了。
      • @ZhenYang 在添加 const 限定符的情况下,参数看起来像一个常量数组,但参数不是常量数组。所以将参数声明为引用的函数被调用。
      • @ZhenYang 至于 begin 和 end 那么它们不能应用于指针。
      • 好的,coliru.stacked-crooked.com可以传,但是用VS 2015就传不上了。我觉得这样的代码最好不要写。
      【解决方案3】:

      数组衰减为指针,这就是存在歧义的原因 - 但您可以收到对 10 个 ints 数组的引用,如下所示:

      void fun(int (&arr)[10]) {
          std::cout << std::end(arr) - std::begin(arr) << "\n";
      }
      

      对于更通用的方法,接受任何类型和大小的数组:

      #include <iostream>
      #include <iterator>
      
      template<typename T, size_t N>
      void fun(T (&arr)[N]) {
          std::cout << "called with an array of " << N << " elements\n";
          std::cout << std::end(arr) - std::begin(arr) << "\n";
      }
      
      int main() {
          int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
          fun(arr);
      }
      

      【讨论】:

        【解决方案4】:
        typedef int arrTen[10];
        
        int main()
        {
           int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
            arrTen arr1;
        
           return 0;
        }
        

        这是 arr 和 arrTen 的 typeid() 的打印

        (gdb)p typeid(arr))
        找不到“int [10]”的类型信息符号
        (gdb)p typeid(arr1))
        找不到“int [10]”的类型信息符号

        这意味着编译器是相同的,这就是为什么你有错误

        【讨论】:

          猜你喜欢
          • 2015-03-16
          • 1970-01-01
          • 2016-12-27
          • 2022-11-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-12-09
          相关资源
          最近更新 更多