Why doesn't the default argument allow const member?

A

Alf P. Steinbach

* Bart van Ingen Schenau:
The problem is that the default argument expression is 'evaluated' in
two different contexts. At the point where the expression occurs in
the sourcce code, it is 'evaluated'to determine which objects and
functions are referenced. At this point, all identifiers must be bound
to an actual object or function.
When the default argument is used in a function call, then it is
evaluated for a second time, not to determine the actual value of the
default argument.

For example:

int a = 0;
int f(double x) {return x*100;}
int g(int x=f(a)); /* binds default argument to f(double) and ::a */

int f(int x) {return x;}

void m()
{
int a = 1;
::a = 2;
g(); /* calls g(f(2)) -> g(200) */
}

As you can see, this mechanism precludes using information that is
only known at the call site (such as the object on which a member-
function is being invoked).

On the contrary, I don't see any technical problem implementing a default
argument mechanism that references 'this'.

It would be the only place in C++ where a default argument expression can
reference another argument, and would require the 'this' argument to be
evaluated first at least when there is such a default argument expression, but
'this' is treated specially in many other ways.

However, one might rationalize the existing rule as a consistency thing, that a
default argument expression is not allowed to reference other arguments, and/or
one might rationalize the existing rule as allowing any order of actual argument
evaluation (which I don't agree is a good thing, but it's been there since K&R).


Cheers,


- Alf
 
D

Divick

[snipped problem, why the compiler does not allow const member
variables as default arguments, see OP's example:
class x
{
private :
  const int a;
public :
  X() : a(1)
  {}
  void foo(int arg = a)
  {
    cout << arg << endl;
  }};
Having looked at the various posts, I feel that this discussion is
still inconclusive and I would agree with Fred that it is simply not
allowed by the standard, probably to ease the job of compiler writer.
I think that may be the main reason. Since the computation of the
default argument could be an arbitrarily complex expression, it would
be quite a pain to fully parse such code (although I see no technical
reason why this should be impossible), so I guess that the C writers
decided to allow only literals (there is still enough compiler work
necessary to convert the literals to the actual paramter type: you can
provided char* literals for std::string parameters).

Actually, you ARE allowed to use an arbitrarily complex expression as
a default argument.
The problem is that the default argument expression is 'evaluated' in
two different contexts. At the point where the expression occurs in
the sourcce code, it is 'evaluated'to determine which objects and
functions are referenced. At this point, all identifiers must be bound
to an actual object or function.
When the default argument is used in a function call, then it is
evaluated for a second time, not to determine the actual value of the
default argument.

For example:

int a = 0;
int f(double x) {return x*100;}
int g(int x=f(a)); /* binds default argument to f(double) and ::a */

int f(int x) {return x;}

void m()
{
  int a = 1;
  ::a = 2;
  g(); /* calls g(f(2)) -> g(200) */

}

On my compiler the call to g(f(2)) calls g(2), which makes sense as
the type of a passed to f is 'known' at compile time to be int and
thus the integer version of f() is called. While you seem to suggest
that the double version of f() is called. Was it a typo?
As you can see, this mechanism precludes using information that is
only known at the call site (such as the object on which a member-
function is being invoked).

I did not get what you mean here. Could you please elaborate? If you
mean that all the information is not available at compile time in this
example, I would tend to disagree.

I think you can safely assume that they only do so if the language has
built-in (specified in the language definition) support for multi-
threading.

How about C++, does C++ spec support multi threading?

Thanks,
DK
 
D

Divick

* Bart van Ingen Schenau:











On the contrary, I don't see any technical problem implementing a default
argument mechanism that references 'this'.

It would be the only place in C++ where a default argument expression can
reference another argument, and would require the 'this' argument to be
evaluated first at least when there is such a default argument expression, but
'this' is treated specially in many other ways.

However, one might rationalize the existing rule as a consistency thing, that a
default argument expression is not allowed to reference other arguments, and/or
one might rationalize the existing rule as allowing any order of actual argument
evaluation (which I don't agree is a good thing, but it's been there since K&R).

I think I am unable to follow here. I would be really thankful if you
could elaborate.

Thanks & Regards,
Divick
 
A

Alf P. Steinbach

* Divick:
I think I am unable to follow here. I would be really thankful if you
could elaborate.

Huh?

Anyway, I'll assume the problem is that you're unfamiliar with the internal
workings of method calls, where "method" means a non-static member function that
is not a constructor.

Given a method call such as

o.m( a1, a2, a3 )

machine code (or bytecode, whatever) must be generated to pass four arguments,
namely o, a1, a2, and a3.

Typically o is special-cased, passed in a register, while a1, a2 and a3 are
passed on the machine stack. Other schemes are also used. The details don't
matter, except that at the machine code level all argument passing reduces to
passing *values*.

And so what's passed is &o, which inside the call becomes the 'this' pointer.

If any of the aN arguments have default expressions that depend on the 'this'
pointer, then &o must be evaluated first.

The current rules disallow such a dependency, and so with the current rules the
arguments can be evaluated in any order.


Cheers & hth.,

- Alf
 
B

Bart van Ingen Schenau

On my compiler the call to g(f(2)) calls g(2), which makes sense as
the type of a passed to f is 'known' at compile time to be int and
thus the integer version of f() is called. While you seem to suggest
that the double version of f() is called. Was it a typo?

No, it was a misunderstanding on my side of the wording in the C++
standard.
The most important part is that, at the point of the declaration of
the default argument, a function called 'f' and an object called 'a'
must be available.

Perhaps it was better if I just copied the example from the standard
(instead of trying to make up something similar):

int a = 1;
int f(int);
int g(int x = f(a)); // default argument: f:):a)

void h() {
a = 2;
{
int a = 3;
g(); // g(f:):a))
}
}

Note that this example also calls g(f(2)).
I did not get what you mean here. Could you please elaborate? If you
mean that all the information is not available at compile time in this
example, I would tend to disagree.

I mean that not all required information to access a non-static member
is available at compile-time *at the source location where it is
needed*.
The way that default arguments are defined, you need a concrete object
(as in: the address is/can be fully known) at the point where the
default argument is declared.
The name 'this', or an expression that implicitly depends on 'this',
does not qualify as a concrete object.

It could have been defined otherwise (for example, that name-lookup is
performed at the call-site), but I don't expect the rules to chane
now, becaus that would have too big an impact.
How about C++, does C++ spec support multi threading?

The current version of the C++ standard does NOT support multi-
threading.
Support will be added in the upcoming revision.
Thanks,
DK

Bart v Ingen Schenau
 
D

Divick

* Divick:





Huh?

Anyway, I'll assume the problem is that you're unfamiliar with the internal
workings of method calls, where "method" means a non-static member function that
is not a constructor.

Given a method call such as

    o.m( a1, a2, a3 )

machine code (or bytecode, whatever) must be generated to pass four arguments,
namely o, a1, a2, and a3.

Typically o is special-cased, passed in a register, while a1, a2 and a3 are
passed on the machine stack. Other schemes are also used. The details don't
matter, except that at the machine code level all argument passing reduces to
passing *values*.

And so what's passed is &o, which inside the call becomes the 'this' pointer.

If any of the aN arguments have default expressions that depend on the 'this'
pointer, then &o must be evaluated first.

The current rules disallow such a dependency, and so with the current rules the
arguments can be evaluated in any order.

Cheers & hth.,

- Alf

That very well makes sense. If I understood correctly then the key is:
argument evaluation order is not dictated by the spec and hence any
argument dependent on 'this', would mean having a specific evaluation
order.

Thanks & Regards,
DK
 
D

Daniel Pitts

Francesco said:
If I got it right, members functions aren't much different from normal
functions, that is, you should think of your "foo(int)" as something
like "foo(int, A* this)", because the function itself is not aware of
the object it is called upon and needs the "hidden 'this' argument" to
be aware of it.

Unfortunately you cannot even write something like "void foo(int arg =
this->a)", because the 'this' pointer is visible only inside of the
function's body.

If you need a workaround, one way could be to decide a range of values
that lead to use the const value, such as:
-------
class A {
const int i;
public:
A(int v = 0) : i(v) {}
int foo(int v = -1) {
if(v < 0) {
return i;
} else {
return v;
}
}
};
-------
Or a different work around:

void foo(int arg) {
std::cout << arg << std::endl;
}
void foo() {
foo(a);
}
 
F

Francesco S. Carta

Or a different work around:

void foo(int arg) {
    std::cout << arg << std::endl;}

void foo() {
   foo(a);

}

Right, thanks, but that has already been pointed out and acknowledged.

Follow the threads thoroughly please.

Have good time,
Francesco
 
D

Daniel Pitts

Francesco said:
Right, thanks, but that has already been pointed out and acknowledged.

Follow the threads thoroughly please.

Not every message is sent and received instantly. It was not
pointed-out and acknowledge in my view.
 
F

Francesco S. Carta

Not every message is sent and received instantly.  It was not
pointed-out and acknowledge in my view.

Fine, please accept my apologies.

Taking in account that both of my posts (the one you were replying to
and the one where I acknowledged the better workaround) have been
posted circa one week ago from the same identical newsreader
(GoogleGroups), coming to know that you received the latter with a one-
week delay seems a bit odd to me.

But these are just lucubrations of mine and I must trust your word.

My apologies, once again.

Have good time,
Francesco
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,216
Latest member
topweb3twitterchannels

Latest Threads

Top