Why doesn't the default argument allow const member?

D

Divick

Hi,

I have the following code (see below). My compiler g++ 3.4.6 doesn't
compile it. I wonder why shouldn't this be allowed? I understand that
const int a, would be different for different objects, unlike static
const. And functions are common for all objects. But from the
compiler's perspective, shouldn't it be simple to handle as for every
invocation of a function with default argument, if the value is not
supplied then the compiler can simply initialize the function with the
value on which the function has been called?

class x
{
private :
const int a;
public :
X() : a(1)
{}

void foo(int arg = a)
{
cout << arg << endl;
}

};

Thanks in advance,
-DK
 
T

Thomas Lenz

Divick said:
Hi,

I have the following code (see below). My compiler g++ 3.4.6 doesn't
compile it. I wonder why shouldn't this be allowed? I understand that
const int a, would be different for different objects, unlike static
const. And functions are common for all objects. But from the
compiler's perspective, shouldn't it be simple to handle as for every
invocation of a function with default argument, if the value is not
supplied then the compiler can simply initialize the function with the
value on which the function has been called?

class x
{
private :
const int a;
public :
X() : a(1)
{}

void foo(int arg = a)
{
cout << arg << endl;
}

};

Thanks in advance,
-DK

Is it just a typo, or is your class name x but your Ctor (capital) X ?
 
F

Francesco S. Carta

Hi,

I have the following code (see below). My compiler g++ 3.4.6 doesn't
compile it. I wonder why shouldn't this be allowed? I understand that
const int a, would be different for different objects, unlike static
const. And functions are common for all objects. But from the
compiler's perspective, shouldn't it be simple to handle as for every
invocation of a function with default argument, if the value is not
supplied then the compiler can simply initialize the function with the
value on which the function has been called?

class x
{
private :
const int a;
public :
X() : a(1)
{}

void foo(int arg = a)
{
cout << arg << endl;
}

};

Thanks in advance,
-DK

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;
}
}
};
-------

For what is worth, hope that helps.

Have good time,
Francesco
 
R

Ron

Hi,

I have the following code (see below). My compiler g++ 3.4.6 doesn't
compile it. I wonder why shouldn't this be allowed?

It's not allowed. The default arg substitution is done by the
caller.
 
B

Bo Persson

Divick said:
Hi,

I have the following code (see below). My compiler g++ 3.4.6 doesn't
compile it. I wonder why shouldn't this be allowed? I understand
that const int a, would be different for different objects, unlike
static const. And functions are common for all objects. But from the
compiler's perspective, shouldn't it be simple to handle as for
every invocation of a function with default argument, if the value
is not supplied then the compiler can simply initialize the
function with the value on which the function has been called?

class x
{
private :
const int a;
public :
X() : a(1)
{}

void foo(int arg = a)
{
cout << arg << endl;
}

};

It is not actually a bad idea, however it is expressly forbidden by
the standard. Default arguments cannot use non-static members, or the
keyword 'this'.

If you make a static it will work, but of course share its value
between all x objects.


As a workaround, you can add an overload that gets the correct value:

void foo()
{
foo(a);
}


Bo Persson
 
F

Francesco S. Carta

It is not actually a bad idea, however it is expressly forbidden by
the standard. Default arguments cannot use non-static members, or the
keyword 'this'.

If you make a static it will work, but of course share its value
between all x objects.

As a workaround, you can add an overload that gets the correct value:

     void foo()
     {
          foo(a);
     }

Which is way better than my proposition, thanks for pointing this out
Bo.

Have good time,
Francesco
 
D

Divick

It is not actually a bad idea, however it is expressly forbidden by
the standard. Default arguments cannot use non-static members, or the
keyword 'this'.

If you make a static it will work, but of course share its value
between all x objects.

As a workaround, you can add an overload that gets the correct value:

     void foo()
     {
          foo(a);
     }

Bo Persson

I think I could guess that it is not allowed by the standard, but my
question was originally about why it is not allowed. Unless there is a
good reason for disallowing it in the standard, the standard could
have allowed non static default argument as well.

Thanks for the replies,
DK
 
D

Divick

It's not allowed.   The default arg substitution is done by the
caller.

The question is why is it not allowed? As long as arguments are known
at compile time, the compiler could easily substitute the default
argument be it static or non static.

Thanks for the reply,
DK
 
B

Bart van Ingen Schenau

The question is why is it not allowed? As long as arguments are known
at compile time, the compiler could easily substitute the default
argument be it static or non static.

Really?
How does the compiler know what to substitute in these lines:

int main()
{
x foo, bar;
foo.foo();
bar.foo();
}

Remember that the default argument 'a' must be accessible from the
context of main().
Thanks for the reply,
DK

Bart v Ingen Schenau
 
F

Fred Zwarts

Bart van Ingen Schenau said:
Really?
How does the compiler know what to substitute in these lines:

int main()
{
x foo, bar;
foo.foo();
bar.foo();
}

Remember that the default argument 'a' must be accessible from the
context of main().

Could you elaborate?
If the non-static member function "foo ()" of "x" is accessible by the compiler from the context of main (),
it means that the declaration of "x" is within the context of main.
The declaration of "x" includes the default parameter value "a" for the function "foo ()".
Why then should the non-static member "a" of "x" be inaccessible by the compiler from the context of main?
 
B

Bo Persson

Divick said:
I think I could guess that it is not allowed by the standard, but my
question was originally about why it is not allowed. Unless there
is a good reason for disallowing it in the standard, the standard
could have allowed non static default argument as well.

The standard usually doesn't give any reason for the rules, it just
states what they are.

In this case, I agree that it would be feasible for the caller to
supply not only the 'this' pointer, but also 'this->a' as a parameter
value (with the exception that as a is private, it is a bit
questionable if it should :).

I can only imagine that as the 'this' pointer is formally provided by
magic inside the member function, perhaps we cannot require that the
caller knows what it is, so in theory there are cases where it cannot
provide this->a.


Bo Perssob
 
B

Bart van Ingen Schenau

Could you elaborate?
If the non-static member function "foo ()" of "x" is accessible by the compiler from the context of main (),

It isn't. The name of the member function is only looked up within the
scope of class x. In this case, the compiler knows to look in the
class x due to the use of the member-access operator (operator.) on an
object of type x.
it means that the declaration of "x" is within the context of main.
The declaration of "x" includes the default parameter value "a" for the function "foo ()".
Why then should the non-static member "a" of "x" be inaccessible by the compiler from the context of main?

Lets put it in another way. The main function above is semantically
equivalent to this one:

int main()
{
x foo, bar;
foo.foo(a);
bar.foo(a);
}

Which 'a' am I referring top here?

Bart v Ingen Schenau
 
F

Fred Zwarts

Bart van Ingen Schenau said:
...

Lets put it in another way. The main function above is semantically
equivalent to this one:

int main()
{
x foo, bar;
foo.foo(a);
bar.foo(a);
}

The C++ standard does not allow it, so it is speculation what the sematically equivalent would be if it would allow it.
I assume if the C++ standard would allow it, the semantically equivalent would be:

int main ()
{
x foo, bar;
foo.foo(foo.a);
bar.foo(bar.a);
}

I see no reason why this could not be defined as such.
All the information needed by the compiler for this interpretation is available.
Which 'a' am I referring top here?

This question is then no longer relevant.
 
B

Balog Pal

The C++ standard does not allow it, so it is speculation what the
sematically >equivalent would be if it would allow it.
I assume if the C++ standard would allow it, the semantically equivalent
would >be:
int main ()
{
x foo, bar;
foo.foo(foo.a);
bar.foo(bar.a);
}

Hardly. The context of name lookup at that spot is clearly that of the
function block, and going outside to namespaces. foo and bar only
influences the picture through ADL, but that will not pick any names from
inside foo's type either.
I.e. if you have

struct x { enum e { v1, v2, v3 }; void f(e); } ;
int main () {
x foo;
foo.f(v1); // not good
foo.f(x::v1); //qualification needed!
}

At the spot if you want anything from inside, you must say so. Certainly if
you are definig a member function, the situation is different.
 
D

Divick

The standard usually doesn't give any reason for the rules, it just
states what they are.

In this case, I agree that it would be feasible for the caller to
supply not only the 'this' pointer, but also 'this->a' as a parameter
value (with the exception that as a is private, it is a bit
questionable if it should :).

I can only imagine that as the 'this' pointer is formally provided by
magic inside the member function, perhaps we cannot require that the
caller knows what it is, so in theory there are cases where it cannot
provide this->a.

Bo Perssob

Could you post some example /cases where it doesn't know what this->a
is? In earlier post by Bart, he points to the fact that variable a is
inaccessible in the scope of main because it is private. But here we
are not talking about visibility at the language level but visibility
at the compiler level. Compiler does have knowledge / visibility of
this->a, while in the context of language / scope, surely the variable
is not accessible. I would assume that default arguments are something
that are dealt by the compiler and during the runtime.
 
D

Divick

The C++ standard does not allow it, so it is speculation what the sematically equivalent would be if it would allow it.
I assume if the C++ standard would allow it, the semantically equivalent would be:

int main ()
{
x foo, bar;
foo.foo(foo.a);
bar.foo(bar.a);

}

I see no reason why this could not be defined as such.
All the information needed by the compiler for this interpretation is available.


This question is then no longer relevant.

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 the other angle to look at this problem can be from a
perspective of multi-threading. I am not aware if language designers
keep multi threading issues while designing languages or not, but it
appears that default argument with non static member variable could be
an issue in multi-threading environments. For example on a multi
processor system if one thread is writing to the variable and the
other one is reading the same variable to be passed as default
argument, without being protected by a lock, it can lead to different
behaviors / data passed based on who reads / writes first. But if the
programmer is aware of this issue then from a language design
perspective I guess it is not an issue.

Does anyone have any ideas, thoughts if this could be the reason?

Thanks to all for the earlier replies,
DK
 
D

Divick

Could you post some example /cases where it doesn't know what this->a
is? In earlier post by Bart, he points to the fact that variable a is
inaccessible in the scope of main because it is private. But here we
are not talking about visibility at the language level but visibility
at the compiler level. Compiler does have knowledge / visibility of
this->a, while in the context of language / scope, surely the variable
is not accessible. I would assume that default arguments are something
that are dealt by the compiler and during the runtime.

Please read that last line as

I would assume that default arguments are something that are dealt by
the compiler and NOT during the runtime.
 
B

Bo Persson

Divick said:
Please read that last line as

I would assume that default arguments are something that are dealt
by the compiler and NOT during the runtime.

No, I don't have any good examples. However, I could imagine that with
multiple inheritance and virtual base classes, perhaps the caller
cannot always determine the value of the 'this' pointer. The standard
only requires it to be present INSIDE the called function.


Bo Persson
 
S

Stuart Redmann

[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).

If you really wanted to provide more elaborated default argument
computations you could easily split the method into two methods.
I think the other angle to look at this problem can be from a
perspective of multi-threading. I am not aware if language designers
keep multi threading issues while designing languages or not, but it
appears that default argument with non static member variable could be
an issue in multi-threading environments. For example on a multi
processor system if one thread is writing to the variable and the
other one is reading the same variable to be passed as default
argument, without being protected by a lock, it can lead to different
behaviors / data passed based on who reads / writes first.
Right.

But if the
programmer is aware of this issue then from a language design
perspective I guess it is not an issue.

True. Keeping things in synch (under C) is definitely the responsibily
of the programmer, so this consideration is certainly not the reason.

Regards,
Stuart
 
B

Bart van Ingen Schenau

[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) */
}

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).
If you really wanted to provide more elaborated default argument
computations you could easily split the method into two methods.

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.
Regards,
Stuart

Bart v Ingen Schenau
 

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

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top