为下标运算符和函数调用运算符提供默认参数

12
在以下代码中,我为数组下标运算符提供了默认参数。
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
    }
};

它运行良好。

所以,我有一个问题:

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

5
函数调用可以有任意数量的参数,而数组下标始终只有一个。 - interjay
5
我无法想象“默认下标”有任何用途。 - molbdnilo
1
在这种情况下,x[]代表什么意思? - glglgl
1
@molbdnilo:默认下标会使some_array[]成为一个有效的表达式,并且可能允许类似于PHP的$array[] = 'a new value tacked on to the end';这样的操作。 - cHao
4个回答

14

标准非常明确。

下标运算符重载运算符不允许使用默认参数。

除了下面明确说明的情况外,运算符函数不能有默认参数。与相应运算符所需的参数数量一样,运算符函数不能具有更多或更少的参数,如本子句的其余部分所述。

以及

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

而对于函数调用运算符

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

重载运算符试图遵循内置运算符的相同行为;对于内置下标运算符,始终需要(唯一的)索引,它没有默认参数。因此,重载运算符也不允许有默认参数。另一方面,函数始终可以接受任意数量的参数并具有默认参数。


2
这个答案比我的更好。它从标准中给出了明确的声明,这总是优先于语法规范。 - Jodocus
@Jodocus 语法规范确实得出了相同的结论。幸运的是,在标准中有如此明确的陈述。 - songyuanyao
1
没错,但是考虑一下相反的情况,即语法允许某些在标准中明确禁止的东西。这种情况存在,并且有一个很好的理由说明为什么明确语句总是比语法语句更强。 - Jodocus
@Jodocus:“显式语句总是比语法语句更强大” - 这在标准中有提到吗?我以前从未注意过。听起来在正式纠正之前可能存在歧义。 - Tony Delroy
@Jodocus:我明白了。我不确定你所谓的“胜利”对于这里来说是否最有用——一个特定编译器在标准中模棱两可的部分做什么并不能告诉你另一个编译器可能会做什么。当存在歧义时,你不能信任所有编译器供应商都能发现它并优先考虑非语法规定:歧义将被提出作为缺陷,并给出如何处理它的建议。 - Tony Delroy
显示剩余2条评论

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

主要是因为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 )

大括号的参数是可选的,中括号则不是。 如评论所建议的那样,请注意中括号需要精确地使用一个参数,而大括号可以使用任意数量的参数。

还要考虑songyuanyao的答案,其中包含了官方规范的明确说明。


8
这个回答很好。为了让英文更加易读,可以改进一下,指出 [] 必须恰好接受 1 个参数,且不能多也不能少。 - Yakk - Adam Nevraumont

2
我想你真正想问的是为什么标准允许一种而不允许另一种。原因主要与人们的期望有关,而不是某些技术逻辑排除了一种情况而不是另一种情况:
这是关于人们对[]运算符的预期意义。通常它意味着“获取[...]中的元素”,其中我们使用int或其他类型来查询集合的成员。我们总是对特定成员进行询问,并且我们始终有一个具体的查询目的。
现在考虑默认参数的含义。通常它意味着“您可以指定此参数,但如果没有指定,我将假定默认值”。这对某些函数非常有效,并且人们已经习惯了它。
改变这个可能会让很多人在看到int x = vec[]时感到困惑。

1
运算符(包括用户定义类型和内置类型的重载)旨在让人们使用数学、逻辑和一般用法中熟悉的符号表示法(尽管<<和>>已经被用作流操作符,鉴于计算机上普遍可用的有限字符,必须做出符号妥协)。反过来允许对直觉、熟悉符号表示法(如隐含参数)进行变化似乎是适得其反的。
然而,operator()不同,它的存在是为了抽象出通过对象调用和调用硬编码函数之间的差异 - 它需要支持默认参数才能正确地执行该操作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接