【问题标题】:C++ compiler does not warn about missing argument before end of default argumentsC++ 编译器在默认参数结束之前不会警告缺少参数
【发布时间】:2015-03-04 18:33:42
【问题描述】:

我用 3 个参数声明了一个 C++ 函数声明,其中两个具有类似的默认值。

void func(int const n, bool const flag=true, int *array=NULL) {
  /* print contents of array */
}

当我错误地调用函数时,省略了第二个参数,但包括了第三个参数,就像这样

int array[5]={1,2,3,4,5};
func(5,array);

gcc 和 intel 编译器(Ubuntu 14.04 LTS 上的默认编译器)都没有抱怨指定了最后一个参数而没有指定倒数第二个。代码运行了,但为数组发送了 NULL(我预计代码会失败)。

我的问题是为什么编译器没有抱怨它找不到匹配的函数,因为我的调用签名应该显示为

funct(int const, int *)

在编译期间我可以打开哪些选项来触发有关此错误用法的警告?

【问题讨论】:

  • 你应该启用“代码审查员因为添加默认参数而对你大喊大叫”警告:)
  • 据我所知,没有任何警告。 Array->pointer->bool 是一个完美定义的转换序列。
  • 这是一个高度简化但具有代表性的示例,由更复杂的代码构造而成,如果我避免使用默认值的参数,我将为所有变体提供如此多的重载函数,这将非常笨拙。
  • 是否有任何机制可以防止参数进行隐式转换。我计划使用@Slava 建议的方法(即切换参数的顺序),但是在我较大的代码的上下文中,如果我可以在指针参数之前保留布尔参数的默认值,它在语义上会更加清晰和清晰使用默认值并保护指针参数免于显式转换。

标签: c++ compiler-warnings default-arguments


【解决方案1】:

编译器将数组衰减为指针并将指针转换为bool 并继续。

更新

来自 C++11 标准:

4 次标准转换 [conv]

1 标准转换是具有内置含义的隐式转换。第 4 条列举了完整的此类 转换。 标准转换序列是按以下顺序的标准转换序列:

- 从以下集合进行零次或一次转换:左值到右值转换,数组到指针转换, 以及函数到指针的转换。

— 从以下集合中进行零次或一次转换:整数提升、浮点提升、积分 转换,浮点转换,浮点整数转换,指针转换,指针 成员转换和布尔转换。

— 零个或一个资格转换。

[ 注意:一个标准的转换序列可以是空的,也就是说,它可以不包含任何转换。 ——尾注 ]

如果需要将标准转换序列应用于表达式以将其转换为所需的目标类型。

【讨论】:

  • 这就是为什么默认参数如此可怕。
  • 是否有 2 个转换数组 -> ponter -> bool ?
  • @Slava,这是我的理解。
  • @Slava:一个数组衰减到一个指针,所以我不确定这是否考虑到它本身的转换。然后可以将指针转换为布尔值。
  • @NathanOliver,是的,数组到指针是按照标准进行的转换。令人难以置信的是,它被称为数组到指针的转换。
【解决方案2】:

根据 C++ 标准(4 种标准转换)

1 标准转换是具有内置含义的隐式转换。 第 4 条列举了所有此类转换。一个标准 转换序列是标准转换的序列 以下顺序:

- 来自以下集合的零次或一次转换: 左值到右值的转换,数组到指针的转换,和 函数到指针的转换。

- 来自以下集合的零或一个转换:积分 促销,浮点促销,积分转换,浮动 点转换,浮点整数转换,指针转换, 指向成员转换的指​​针,以及布尔转换

— 零个或一个资格转换。

4.12 布尔转换

1 算术纯右值、无范围枚举、指针或指针 to 成员类型可以转换为 bool 类型的纯右值。一个零 值、空指针值或空成员指针值是 转换为假;任何其他值都将转换为 true。为了 直接初始化 (8.5),std::nullptr_t 类型的纯右值可以是 转换为 bool 类型的纯右值;结果是假的。

所以这个函数调用

func(5,array);

等价于

func(5,array, NULL);

第二个参数首先从数组转换为指针(数组到指针的转换),然后再转换为布尔真(布尔转换)。

因此,此调用是对该函数的有效调用。编译器将参数隐式转换为适当的类型。

【讨论】:

    【解决方案3】:

    发生了对 bool 的隐式转换。 使用 Visual C++,您会收到 C4800 警告。 在 gcc 中,您可以使用以“-W”开头的选项请求特定警告,例如 -Wimplicit 请求隐式声明的警告。有关完整文档,请参阅 Options to Request or Suppress Warnings

    【讨论】:

      【解决方案4】:

      在这种特殊情况下,解决方案可能使用不同的参数顺序:

      void func(int const n, int *array=NULL, bool const flag=true ) {
        /* print contents of array */
      }
      

      如果你省略第二个参数,布尔值不能被隐式转换为指针(除非你有使用 0 作为false 的坏习惯),所以它将无法编译(或至少提供警告)。

      void func(int const n, int *array = nullptr, bool const flag=true ) {
            /* print contents of array */
      }
      
      int main()
      {
          func( 0, false );
      }
      

      警告:将 'false' 转换为 'void 参数 2 的指针类型 func(int, int*, bool)' [-Wconversion-null]

      但总的来说,它表明您应该使用不同的技术。例如使用std::vector 代替原始指针。

      typedef std::vector<int> int_vec;
      void func( bool const flag=true, const int_vec &array = int_vec() ) {
            /* print contents of array */
      }
      
      int main()
      {
          int_vec array {1,2,3,4,5};
          func( array );
      }
      

      错误:无法将 'int_vec {aka std::vector}' 转换为 'bool' for 参数 '1' 到 'void func(bool, int_vec)'

      【讨论】:

      • 如果使用0作为空指针,你至少可以让GCC警告你应该使用nullptr
      猜你喜欢
      • 2011-08-10
      • 1970-01-01
      • 2012-03-04
      • 1970-01-01
      • 1970-01-01
      • 2021-01-30
      • 1970-01-01
      • 1970-01-01
      • 2017-05-05
      相关资源
      最近更新 更多