【问题标题】:Provide default arguments for subscript operator and function call operator为下标运算符和函数调用运算符提供默认参数
【发布时间】:2018-04-28 16:32:53
【问题描述】:

在下面的代码中,我为数组下标运算符提供了默认参数

struct st 
{
    int operator[](int x = 0)
    {
        // code here
    }
};

但是,编译器产生了一个错误:

error: 'int st::operator[](int)' cannot have default arguments
     int operator[](int x = 0)

但是,如果我为 函数调用 运算符提供默认参数。

struct st 
{
    int operator()(int x = 0)
    {
        // code here
    }
};

一切正常。

所以,我有一个问题:

  • 为什么不允许数组下标运算符的默认参数?
  • 为什么函数调用运算符允许使用默认参数?

【问题讨论】:

  • 因为函数调用可以有任意数量的参数,而数组下标总是有一个。
  • 我无法想象“默认下标”有什么用处。
  • 在这种情况下x[] 是什么意思?
  • @molbdnilo:默认下标将使some_array[] 成为一个有效的表达式,并且可能允许像PHP 的$array[] = 'a new value tacked on to the end'; 这样的东西。

标签: c++ operator-overloading operators language-lawyer default-arguments


【解决方案1】:

标准规定得很清楚。

operator overloading 中的subscripting operator 中不允许使用默认参数。

运算符函数不能有默认参数,除非下面明确说明。运算符函数的参数不能多于或少于相应运算符所需的数量,如本小节其余部分所述。

operator[] 应该是一个只有一个参数的非静态成员函数。

function call operator

operator() 应该是具有任意数量参数的非静态成员函数。它可以有默认参数。

重载的操作符尝试遵循内置操作符的相同行为;使用内置的下标运算符,(只有一个)索引始终是必需的,它没有默认参数。那么重载的运算符也不允许有默认参数。另一方面,函数总是可以接受任意数量的参数并具有默认参数。

【讨论】:

  • 这个答案比我的好。它给出了标准中的明确声明,始终高于语法规范。
  • @Jodocus 语法规范肯定得出同样的结论。很幸运,标准中有如此明确的陈述。
  • 确实如此。但是考虑相反的情况,即语法允许标准中明确禁止的内容。这些情况是存在的,并给出了一个明确的理由为什么显式语句总是比语法语句更强大。
  • @Jodocus “显式语句总是比语法语句强” - 它是否在标准中的某个地方这么说?我以前从未注意到它。在正式纠正之前,这听起来有点模棱两可。
  • @Jodocus 同意。但是这些陈述可能会令人困惑和模糊;在这方面,我认为语法规范最终获胜。
【解决方案2】:
  • 为什么不允许数组下标运算符的默认参数?
  • 为什么函数调用运算符允许默认参数?

主要是因为 C++ 语法是这样说的。根据 A.4 [gram.expr]:

postfix-expression --> postfix-expression [ expr-or-braced-init-list ] 
                   --> postfix-expression ( expression-list opt ) 
                   --> simple-type-specifier (expression-list opt) 
                   --> typename-specifier ( expression-list opt )

大括号的参数是可选的,括号的参数不是。 正如评论中所建议的那样,请注意,括号必须完全采用 one 参数,而大括号可以采用任意数量的参数。

对于标准中的明确声明,还要考虑宋元尧的回答。

【讨论】:

  • 这个答案很棒。注意[] 必须只取 1 个参数,这样才能更清楚地阅读英文,这可能会有所改进。
【解决方案3】:

我想你真的在问为什么标准允许一个而不是另一个。 原因主要与人们的期望有关,而不是某些技术逻辑在一种情况下排除默认值,但在另一种情况下不排除:

这是人们期望 [] 运算符的含义的问题。通常它的意思是“在 [...] 处获取元素”,我们使用 int 或其他类型来对集合的成员进行特定查询。我们总是对询问特定成员感兴趣,并且我们始终牢记特定的询问。

现在考虑默认参数的含义。通常它的意思是“你可以指定这个,但如果没有,我会假设这个默认值”。这对于某些功能非常有效,人们已经习惯了。

改变这个可能会让很多人看到int x = vec[]时挠头

【讨论】:

    【解决方案4】:

    运算符(用户定义类型和内置类型的重载)旨在让人们使用数学、逻辑和一般用法中熟悉的符号(尽管 <<>> 已将双重服务视为流式传输运算符,并且考虑到计算机上几乎普遍可用的有限字符,必须做出符号妥协)。转而允许对直观、熟悉的符号(例如隐含参数)进行变化似乎适得其反。

    operator() 的不同之处在于它可以抽象出“调用”(通过)对象和调用硬编码函数之间的差异 - 它需要支持默认参数才能正确执行此操作。

    【讨论】:

      【解决方案5】:

      从 C++23 (https://eel.is/c++draft/over.sub) 开始,您可以为数组下标运算符设置默认参数

      用户定义类的重载下标运算符现在与定义调用运算符具有相同的语义。因此,您可以有 0、1、2 或多个参数。

      struct st 
      {
          int operator[](int x = 0)
          {
              // code here
          }
      };
      

      st obj { /* ... */ };
      obj[]
      

      现在有效。

      很遗憾,没有编译器支持此功能,因为这是最新批准的功能。

      【讨论】:

        猜你喜欢
        • 2022-01-08
        • 2021-05-28
        • 1970-01-01
        • 2014-01-22
        • 2018-02-17
        • 1970-01-01
        • 1970-01-01
        • 2021-07-19
        • 2019-10-10
        相关资源
        最近更新 更多