非静态成员作为非静态成员函数的默认参数

发布于 2024-10-09 07:27:15 字数 223 浏览 1 评论 0原文

struct X
{
   X():mem(42){}
   void f(int param = mem) //ERROR
   {
      //do something
   }
private: 
   int mem;
};

谁能给我一个理由来说明为什么这在 C++ 中是非法的?!也就是说,我知道这是一个错误,我知道这个错误意味着什么,我只是不明白为什么这是非法的!

struct X
{
   X():mem(42){}
   void f(int param = mem) //ERROR
   {
      //do something
   }
private: 
   int mem;
};

Can anyone give me just one reason as to why this is illegal in C++?! That is to say, I know that it is an error, I know what the error means, I just can't understand why would this be illegal!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

茶色山野 2024-10-16 07:27:16

正如 DeadMG 上面提到的,

void func(int i, int f = g(i))

出于同样的原因,类似的东西也是非法的。然而,我认为这不仅仅是一个愚蠢的限制。为了允许这样的构造,我们需要限制函数参数的求值顺序(因为我们需要在 this->mem 之前计算它),但 C++ 标准明确拒绝对求值顺序的任何假设。

As DeadMG has mentioned above, somethig like

void func(int i, int f = g(i))

is illegal for the same reason. i suppose, however, that it is not simply a silly restriction. To allow such a construction, we need to restrict evaluation order for function parameters (as we need to calculate this before this->mem), but the c++ standard explicitly declines any assumptions on the evaluation order.

一枫情书 2024-10-16 07:27:16

重复问题中接受的答案是为什么,但标准也明确说明了为什么会这样:

8.3.6/9:


示例:以下示例中的 X::mem1() 声明格式不正确,因为没有为用作初始值设定项的非静态成员 X::a 提供对象。

int b;
class X
  int a;
  int mem1(int i = a);    // error: nonstatic member a
                          // used as default argument
  int mem2(int i = b);    // OK: use X::b
  static int b;
};

然而,X::mem2() 的声明是有意义的,因为不需要任何对象来访问静态成员 X::b。类、对象和成员在第 9 节中描述。
"

...并且由于此时不存在语法来提供解析 X::a 值所需的对象,因此实际上不可能使用非静态成员变量作为默认参数的初始值设定项。

The accepted answer in the duplicate question is why, but the standard also explicitly states why this is so:

8.3.6/9:

"
Example: the declaration of X::mem1() in the following example is ill-formed because no object is supplied for the nonstatic member X::a used as an initializer.

int b;
class X
  int a;
  int mem1(int i = a);    // error: nonstatic member a
                          // used as default argument
  int mem2(int i = b);    // OK: use X::b
  static int b;
};

The declaration of X::mem2() is meaningful, however, since no object is needed to access the static member X::b. Classes, objects and members are described in clause 9.
"

... and since there exists no syntax to supply the object necessary to resolve the value of X::a at that point, it's effectively impossible to use non-static member variables as initializers for default arguments.

谁的新欢旧爱 2024-10-16 07:27:16

ISO C++ 第 8.3.6/9 节

非静态成员不得在默认参数表达式中使用,即使它
不求值,除非它作为类成员访问表达式的 id 表达式出现 (5.2.5),或者除非它用于形成指向成员的指针 (5.3.1)。

另请查看该部分中给出的示例。

ISO C++ section 8.3.6/9

a nonstatic member shall not be used in a default argument expression, even if it
is not evaluated, unless it appears as the id-expression of a class member access expression (5.2.5) or unless it is used to form a pointer to member (5.3.1).

Also check out the example given in that section.

随遇而安 2024-10-16 07:27:16

原因之一是,f 是公共的,但 mem 是私有的。因此,像这样的代码:

int main() { 
    X x;
    x.f();
    return 0;
}

...将涉及检索 X 的私有数据的外部代码。

除此之外,它还会(或至少可能)使代码生成变得有点棘手。通常,如果编译器要使用默认参数,它会获取将作为函数声明的一部分传递的值。生成代码以将该值作为参数传递是很简单的。当您可能传递对象的成员(可能任意深度嵌套),然后添加诸如它可能是模板中的从属名称之类的内容时,这可能(例如)通过转换为正确的目标来命名另一个对象类型,并且您有一个使代码生成变得相当困难的秘诀。我不确定,但我怀疑有人考虑过类似的事情,并认为最好保持保守,并且可能稍后开放,如果有充分的理由这样做的话。考虑到我已经多次看到它出现问题,我猜它会在很长一段时间内保持这种状态,因为它很少引起问题。

For one reason, because f is public, but mem is private. As such, code like this:

int main() { 
    X x;
    x.f();
    return 0;
}

...would involve outside code retrieving X's private data.

Aside from that, it would (or at least could) also make code generation a bit tricky. Normally, if the compiler is going to use a default argument, it gets the value it's going to pass as part of the function declaration. Generating code to pass that value as a parameter is trivial. When you might be passing a member of an object (possibly nested arbitrarily deeply) and then add in things like the possibility of it being a dependent name in a template, that might (for example) name another object with a conversion to the correct target type, and you have a recipe for making code generation pretty difficult. I don't know for sure, but I suspect somebody thought about things like that, and decided it was better to stay conservative, and possibly open thins up later, if a good reason was found to do so. Given the number of times I've seen problems arise from it, I'd guess it'll stay the way it is for a long time, simply because it rarely causes problems.

零度℉ 2024-10-16 07:27:16

编译器必须知道地址才能在编译时维护默认值。非静态成员变量的地址在编译时是未知的。

Compiler has to know addresses to maintain default values at compile time. Addresses of non-static member variables are unknown at compile time.

魔法少女 2024-10-16 07:27:16

由于所有其他答案都只是讨论这个问题,我想我会发布一个解决方案。

正如在没有默认参数的其他语言中使用的那样(例如 C# 4.0 之前的版本),

只需使用重载即可提供相同的结果:

struct X
{
   X():mem(42){}
   void f(int param)
   {
      //do something
   }
   void f()
   {
      f(mem);
   }
private: 
   int mem;
};

As all the other answers just discuss the problem, I thought I would post a solution.

As used in other languages without default arguments (Eg C# pre 4.0)

Simply use overloading to provide the same result:

struct X
{
   X():mem(42){}
   void f(int param)
   {
      //do something
   }
   void f()
   {
      f(mem);
   }
private: 
   int mem;
};
徒留西风 2024-10-16 07:27:16

默认参数在不同的上下文中通过两个不同的步骤进行评估。
首先,在声明的上下文中执行默认参数的名称查找。
其次,默认参数的计算是在实际函数调用的上下文中执行的。

为了防止实现变得过于复杂,对可用作默认参数的表达式应用了一些限制。

  • 无法使用具有非静态生存期的变量,因为它们在调用时可能不存在。
  • 不能使用非静态成员变量,因为它们需要(隐式)this-> 限定,而该限定通常无法在调用站点进行评估。

Default arguments are evaluated in two distinct steps, in different contexts.
First, the name lookup for the default argument is performed in the context of the declaration.
Secondly, the evaluation of the default argument is performed in the context of the actual function call.

To keep the implementation from becoming overly complicated, some restrictions are applied to the expressions that can be used as default arguments.

  • Variables with non-static lifetime can't be used, because they might not exist at the time of the call.
  • Non-static member variables can't be used, because they need an (implicit) this-> qualification, which can typically not be evaluated at the call site.
云仙小弟 2024-10-16 07:27:15

您的代码(简化):

struct X
{
   int mem;
   void f(int param = mem); //ERROR
};

您想要使用非静态成员数据作为成员函数参数的默认值。我想到的第一个问题是:默认值 mem 属于类的哪个特定实例

X x1 = {100};  //mem = 100
X x2 = {200};  //mem = 200

x1.f(); //param is 100 or 200? or something else?

您的答案可能是 100,因为 f() 是在具有 mem = 100 的对象 x1 上调用的。如果是这样,那么它需要实现将 f() 实现为:

void f(X* this, int param = this->mem);

这又要求在初始化其他参数之前首先初始化第一个参数。但 C++ 标准没有指定函数参数的任何初始化顺序。因此这是不允许的。出于同样的原因,C++ 标准甚至不允许这样做:

int f(int a, int b = a); //§8.3.6/9

事实上,§8.3.6/9 明确指出,

默认参数分别进行评估
调用函数的时间。 订单
函数参数的求值是
未指定。
因此,参数
函数的不得用于
默认参数表达式,即使
他们没有被评估。


本节的其余部分读起来很有趣。


与“默认”参数相关的有趣主题(尽管与本主题无关):

Your code (simplified):

struct X
{
   int mem;
   void f(int param = mem); //ERROR
};

You want to use a non-static member data as default value for a parameter of a member function. The first question which comes to mind is this : which specific instance of the class the default value mem belongs to?

X x1 = {100};  //mem = 100
X x2 = {200};  //mem = 200

x1.f(); //param is 100 or 200? or something else?

Your answer might be 100 as f() is invoked on the object x1 which has mem = 100. If so, then it requires the implementation to implement f() as:

void f(X* this, int param = this->mem);

which in turn requires the first argument to be initialized first before initialization of other argument. But the C++ standard doesn't specify any initialization order of the function arguments. Hence that isn't allowed. Its for the same reason that C++ Standard doesn't allow even this:

int f(int a, int b = a); //§8.3.6/9

In fact, §8.3.6/9 explicitly says,

Default arguments are evaluated each
time the function is called. The order
of evaluation of function arguments is
unspecified
. Consequently, parameters
of a function shall not be used in
default argument expressions
, even if
they are not evaluated.

And rest of the section is an interesting read.


An interesting topic related to "default" arguments (not related to this topic though):

月下凄凉 2024-10-16 07:27:15

默认参数必须在编译时已知。当您谈论函数调用之类的内容时,即使返回值不是,该函数在编译时也是已知的,因此编译器可以生成该代码,但是当您默认为成员变量时,编译器不会生成该代码。不知道在编译时在哪里找到该实例,这意味着它实际上必须传递一个参数(this)来查找内存。请注意,您不能执行类似 void func(int i, int f = g(i)); 的操作,并且这两者实际上是相同的限制。

我也认为这个限制很愚蠢。但是,C++ 充满了愚蠢的限制。

Default arguments have to be known at compile-time. When you talk about something like a function invocation, then the function is known at compile-time, even if the return value isn't, so the compiler can generate that code, but when you default to a member variable, the compiler doesn't know where to find that instance at compile-time, meaning that it would effectively have to pass a parameter (this) to find mem. Notice that you can't do something like void func(int i, int f = g(i)); and the two are effectively the same restriction.

I also think that this restriction is silly. But then, C++ is full of silly restrictions.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文