Comeau and G++ disagree. Compiler bug?

A

Alan Woodland

Hi,

I was trying to come up with obscure C++ questions in the style of GoTW
and ran across this unexpected difference between G++ and comeau. Can
anyone point me in the direction of the correct result for this program?


#include <iostream>

namespace A {
class Foo { };
struct f {
public:
f(const Foo& f) { std::cout << "ctor" << std::endl; }
};
}

template <typename T>
void f(const T& t) { std::cout << "Template ref" << std::endl; }

void f(A::Foo& f) { std::cout << "Plain ref" << std::endl; }

int main() {
f(A::Foo());
return 0;
}

G++ doesn't accept it and says:
test.cc: In function 'int main()':
test.cc:5: error: 'struct A::f' is not a function,
test.cc:14: error: conflict with 'void f(A::Foo&)'
test.cc:17: error: in call to 'f'
G++ version was:
g++ (GCC) 4.1.3 20070718 (prerelease) (Debian 4.1.2-14)
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Whereas Comeau allows it (I only tried it online, so I don't for certain
know what f(A::Foo()); actually resolved to. Comeau version was:
Comeau C/C++ 4.3.9 (Mar 27 2007 17:24:47) for ONLINE_EVALUATION_BETA1
Copyright 1988-2007 Comeau Computing. All rights reserved.
MODE:strict errors C++ noC++0x_extensions

Thanks,
Alan
 
V

Victor Bazarov

Alan said:
I was trying to come up with obscure C++ questions in the style of
GoTW and ran across this unexpected difference between G++ and
comeau. Can anyone point me in the direction of the correct result
for this program?


#include <iostream>

namespace A {
class Foo { };
struct f {
public:
f(const Foo& f) { std::cout << "ctor" << std::endl; }
};
}

template <typename T>
void f(const T& t) { std::cout << "Template ref" << std::endl; }

void f(A::Foo& f) { std::cout << "Plain ref" << std::endl; }

int main() {
f(A::Foo());
return 0;
}

G++ doesn't accept it and says:
test.cc: In function 'int main()':
test.cc:5: error: 'struct A::f' is not a function,
test.cc:14: error: conflict with 'void f(A::Foo&)'
test.cc:17: error: in call to 'f'
G++ version was:
g++ (GCC) 4.1.3 20070718 (prerelease) (Debian 4.1.2-14)
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There
is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.

Whereas Comeau allows it (I only tried it online, so I don't for
certain know what f(A::Foo()); actually resolved to. Comeau version
was:
Comeau C/C++ 4.3.9 (Mar 27 2007 17:24:47) for ONLINE_EVALUATION_BETA1
Copyright 1988-2007 Comeau Computing. All rights reserved.
MODE:strict errors C++ noC++0x_extensions

AFAICT, G++ is mistaken. It cannot be a construction of a temporary
of type 'f', types are not found using ADL for this syntax. In order
to look 'f' up using ADL, it has to be deemed a function call. By the
time ADL is employed, the syntax 'f(A::Foo())' cannot be "explicit type
conversion (functional notation)". It only can be "type conversion" if
'f' is already deduced as a type, which it cannot be since 'A::f' type
is not visible.

That aside, it cannot be 'f(A::Foo&)' since the reference to a non-const
'A::Foo' cannot be bound to a temporary ('A::Foo()'), which leaves the
only choice: the template. Now, if you change the "plain ref" function
to

void f(A::Foo const&);

then it will be chosen over the template since in rules of the overload
resolution a non-template function is always preferred over a template
instantiation.

V
 
A

Alan Woodland

Victor said:
AFAICT, G++ is mistaken. It cannot be a construction of a temporary
of type 'f', types are not found using ADL for this syntax. In order
to look 'f' up using ADL, it has to be deemed a function call. By the
time ADL is employed, the syntax 'f(A::Foo())' cannot be "explicit type
conversion (functional notation)". It only can be "type conversion" if
'f' is already deduced as a type, which it cannot be since 'A::f' type
is not visible.

That aside, it cannot be 'f(A::Foo&)' since the reference to a non-const
'A::Foo' cannot be bound to a temporary ('A::Foo()'), which leaves the
only choice: the template. Now, if you change the "plain ref" function
to

void f(A::Foo const&);

then it will be chosen over the template since in rules of the overload
resolution a non-template function is always preferred over a template
instantiation.
Thanks,

The non-const reference and temporary was deliberately designed to catch
someone out after a previous question where it had been const and the
version of f in namespace A had been a function to illustrate Koenig
lookup. I wasn't 100% sure it would pick the function over the
constructor for f though.

Am I right in thinking non-const temporary reference binding will be
changing in C++0X? Or am I getting confused? I seem to recall reading
something about it a while back.

I'll be filing a bug against g++ shortly given that g++ (GCC) 4.3.0
20070720 also seems to have this problem.

Alan
 
V

Victor Bazarov

Alan said:
Thanks,

The non-const reference and temporary was deliberately designed to
catch someone out after a previous question where it had been const
and the version of f in namespace A had been a function to illustrate
Koenig lookup. I wasn't 100% sure it would pick the function over the
constructor for f though.

Am I right in thinking non-const temporary reference binding will be
changing in C++0X? Or am I getting confused? I seem to recall reading
something about it a while back.

Not that I know of. There is a new concept being introduced, an rvalue
reference (declaration syntax '&&'). But you can't initialise a regular
reference to non-const object with a temporary, still.
I'll be filing a bug against g++ shortly given that g++ (GCC) 4.3.0
20070720 also seems to have this problem.

I would wait a couple of days... I have a sneaky feeling James Kanze
will want to chime in. <g>

V
 
J

James Kanze

AFAICT, G++ is mistaken. It cannot be a construction of a temporary
of type 'f', types are not found using ADL for this syntax. In order
to look 'f' up using ADL, it has to be deemed a function call. By the
time ADL is employed, the syntax 'f(A::Foo())' cannot be "explicit type
conversion (functional notation)". It only can be "type conversion" if
'f' is already deduced as a type, which it cannot be since 'A::f' type
is not visible.

That may have been the intent---in fact, it probably was, since
the current draft has been changed to state explicitly that ADL
will ignore any names that aren't names of functions. But I'm
not sure that it's that clear in the currently official version
of the standard; it's one of those things which can be
interpreted in different ways. If I interpret the error message
from g++ correctly, it's saying that ADL was applied because
the expression was a function call, and then the results of
function overload resolution meant that it wasn't a function;
the official standard isn't too clear about what to do in this
case, so they treated it as an error.
 
J

James Kanze

I would wait a couple of days... I have a sneaky feeling James Kanze
will want to chime in. <g>

Obviously:). In this case, I don't find the standard as clear
as you do (but your interpretation is definitly one possible
one, and probably the intended one), so I wouldn't treat g++ too
harshly; they may just have interpreted the standard
differently. The current draft, however, adds an additional
qualification to ADL which says, literally "All names except
those of (possibly overloaded) functions and function templates
are ignored." So I'm sure that whatever the original reasons
why g++ behaves as it does, they'll want to change it to conform
to what the future standard will say.
 
B

bnonaj

James said:
Obviously:). In this case, I don't find the standard as clear
as you do (but your interpretation is definitly one possible
one, and probably the intended one), so I wouldn't treat g++ too
harshly; they may just have interpreted the standard
differently. The current draft, however, adds an additional
qualification to ADL which says, literally "All names except
those of (possibly overloaded) functions and function templates
are ignored." So I'm sure that whatever the original reasons
why g++ behaves as it does, they'll want to change it to conform
to what the future standard will say.

--
James Kanze (GABI Software) email:[email protected]
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
I think we're all grateful for both Victor and James. They both make a
lot of valuable contributions to this newsgroup. As for the G++ compiler
flagging an error, that is what I would have expected, and I'm indebted
to Victor and James for pointing out the lack of clarity in the current
C++ standard on this point. Keep up the good work, I certainly
appreciate it and hopefully it improves my C++ knowledge reading the
feedback given on this newsgroup.

JB
 
V

Victor Bazarov

James said:
That may have been the intent---in fact, it probably was, since
the current draft has been changed to state explicitly that ADL
will ignore any names that aren't names of functions. But I'm
not sure that it's that clear in the currently official version
of the standard; it's one of those things which can be
interpreted in different ways. If I interpret the error message
from g++ correctly, it's saying that ADL was applied because
the expression was a function call, and then the results of
function overload resolution meant that it wasn't a function;
the official standard isn't too clear about what to do in this
case, so they treated it as an error.

According to the new draft, if 'f' were an object with an overloaded
function call operator:

namespace A {
class Foo { };
struct ff {
public:
void operator()(const Foo&)
{ std::cout << "operator()" << std::endl; }
} f;
}

void f(const A::Foo&) { std::cout << "regular func" << std::endl; }

int main() {
f(A::Foo());
return 0;
}

, it wouldn't have been found either, but I am not sure about the
current wording (or GNU folks' interpretation of it). I don't have
G++ to try this one, but VC++ and Comeau online accept it.

V
 
A

Alan Woodland

AFAICT, G++ is mistaken. It cannot be a construction of a temporary
According to the new draft, if 'f' were an object with an overloaded
function call operator:

namespace A {
class Foo { };
struct ff {
public:
void operator()(const Foo&)
{ std::cout << "operator()" << std::endl; }
} f;
}

void f(const A::Foo&) { std::cout << "regular func" << std::endl; }

int main() {
f(A::Foo());
return 0;
}

, it wouldn't have been found either, but I am not sure about the
current wording (or GNU folks' interpretation of it). I don't have
G++ to try this one, but VC++ and Comeau online accept it.

Looks like this one doesn't work either:

g++-4.1 -Wall -std=c++98 /tmp/test.cc

/tmp/test.cc: In function 'int main()':
/tmp/test.cc:9: error: 'A::f' is not a function,
/tmp/test.cc:12: error: conflict with 'void f(const A::Foo&)'
/tmp/test.cc:15: error: in call to 'f'

Alan
 
J

James Kanze

[...]
According to the new draft, if 'f' were an object with an overloaded
function call operator:
namespace A {
class Foo { };
struct ff {
public:
void operator()(const Foo&)
{ std::cout << "operator()" << std::endl; }
} f;
}
void f(const A::Foo&) { std::cout << "regular func" << std::endl; }
int main() {
f(A::Foo());
return 0;
}
, it wouldn't have been found either, but I am not sure about the
current wording (or GNU folks' interpretation of it). I don't have
G++ to try this one, but VC++ and Comeau online accept it.

That's a good point. I think that in the current standard, that
is clearly legal. The fact that the draft makes it illegal
could break some code, and possibly wasn't intended. Maybe
someone should raise the point in comp.std.c++. (I'm leaving on
vacation very shortly, and will be off line for close to three
weeks, or I'd do it myself.)
 
V

Victor Bazarov

James said:
[...]
According to the new draft, if 'f' were an object with an overloaded
function call operator:
namespace A {
class Foo { };
struct ff {
public:
void operator()(const Foo&)
{ std::cout << "operator()" << std::endl; }
} f;
}
void f(const A::Foo&) { std::cout << "regular func" << std::endl; }
int main() {
f(A::Foo());
return 0;
}
, it wouldn't have been found either, but I am not sure about the
current wording (or GNU folks' interpretation of it). I don't have
G++ to try this one, but VC++ and Comeau online accept it.

That's a good point. I think that in the current standard, that
is clearly legal. The fact that the draft makes it illegal
could break some code, and possibly wasn't intended. Maybe
someone should raise the point in comp.std.c++. (I'm leaving on
vacation very shortly, and will be off line for close to three
weeks, or I'd do it myself.)

I don't believe it's so bad. Let's examine the "issue". If the
code was broken before the new Standard (the error was reported),
how would suddenly making the code working *break* anything? Are
you thinking of some SFINAE technique here?

If I remove the '::f' function from the code above, VC++ and Comeau
(the ones I can test now) do not find 'f', not sure what G++ would
do. Let's say that G++ would accept it (is that a bug?), relying
on their understanding of ADL. The new standard would then prohibit
finding 'f' since it's not a function. Fixing it should not really
be such a PITA (provided G++ folks do implement the new Standard
correctly after it's adopted), and they could still provide their
old behaviour as another extension <g>.

I'll post the question in comp.std.c++.

V
 

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,768
Messages
2,569,575
Members
45,053
Latest member
billing-software

Latest Threads

Top