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 ?
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
--
Francesco S. Carta, hobbyist
http://fscode.altervista.org
It's not allowed. The default arg substitution is done by the
caller.
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
Which is way better than my proposition, thanks for pointing this out
Bo.
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
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
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
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?
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
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
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.
>>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);
>}
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.
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.
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
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
> 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
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 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
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
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
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
I think I am unable to follow here. I would be really thankful if you
could elaborate.
Thanks & Regards,
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
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)).
>
> > 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 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.
>
> > 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?
The current version of the C++ standard does NOT support multi-
threading.
Support will be added in the upcoming revision.
>
> Thanks,
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
void foo(int arg) {
std::cout << arg << std::endl;
}
void foo() {
foo(a);
}
--
Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>
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.
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.