Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Re: Redeclaration not an error in GCC

95 views
Skip to first unread message

Alf P. Steinbach

unread,
Jan 30, 2018, 9:41:24 AM1/30/18
to
On 1/30/2018 3:16 PM, Stefan Ram wrote:
> I have a version of GCC here that is not too old and does not
> give an error message for the code below.
>
> void f(){}
> //int f();
> int main() { int f(); }
>
> It /does/ give an error message when »//« is removed, however.
> I also have a version of clang, giving an error message for the
> code above.

Ordinary function overloads can't differ in return type only, so that
lack of diagnostic is clearly a compiler bug.

That said, I /think/ this code is valid:

struct S
{
template< class Result >
operator Result() const { return {}; }
};

auto main() -> int
{
S s;
int i = s;
double d = s;
}

Disclaimer: off the cuff code.

Assuming the code is valid the open question is then, where does the
standard allow function template instantiations to differ in return type
only?

And (I don't think so) is there any way to refer to that function with
explicit template parameter?

Cheers!,

- Alf

James R. Kuyper

unread,
Jan 30, 2018, 10:01:17 AM1/30/18
to
On 01/30/2018 09:16 AM, Stefan Ram wrote:
> I have a version of GCC here that is not too old and does not
> give an error message for the code below.
>
> void f(){}

This declaration has file scope.

> //int f();

"Function declarations that differ only in the return type, the
exception specification (18.4), or both cannot be overloaded." (16.1p2).
"A program is ill-formed if it contains two such non-overloadable
declarations in the same scope." (16.1p1).

> int main() { int f(); }

This declaration has block scope, therefore 16.1p1 doesn't apply. Inside
the scope of this declaration, it hides the file scope declaration.
However, 16.1p2 should still apply - it isn't permitted for a function
matching this declaration to be defined in the same program as the first
definition of f() above. It seems to me that this should qualify as a
diagnosable rule, and therefore one that must result in a diagnostic
(4.1p2), but gcc apparently disagrees with me about that.

I modified f() to print some output, and modified main() to print the
value returned by f() - both printouts worked. The returned value was
6295680.

James R. Kuyper

unread,
Jan 30, 2018, 11:51:58 AM1/30/18
to
On 01/30/2018 10:01 AM, James R. Kuyper wrote:
> On 01/30/2018 09:16 AM, Stefan Ram wrote:
>> I have a version of GCC here that is not too old and does not
>> give an error message for the code below.
>>
>> void f(){}
>
> This declaration has file scope.
>
>> //int f();
>
> "Function declarations that differ only in the return type, the
> exception specification (18.4), or both cannot be overloaded." (16.1p2).
> "A program is ill-formed if it contains two such non-overloadable
> declarations in the same scope." (16.1p1).
>
>> int main() { int f(); }
>
> This declaration has block scope, therefore 16.1p1 doesn't apply. Inside
> the scope of this declaration, it hides the file scope declaration.
> However, 16.1p2 should still apply - it isn't permitted for a function
> matching this declaration to be defined in the same program as the first
> definition of f() above. It seems to me that this should qualify as a
> diagnosable rule, and therefore one that must result in a diagnostic
> (4.1p2), but gcc apparently disagrees with me about that.
>
> I modified f() to print some output, and modified main() to print the
> value returned by f() - both printouts worked. The returned value was
> 6295680.

I've been asked to post that code:

#include <iostream>
void f(){
std::cout << "Hello, World!" << std::endl;
}
// int f();
int main() { int f();
std::cout << "f() returns:" << f() << std::endl;
}

Richard

unread,
Jan 30, 2018, 12:49:37 PM1/30/18
to
[Please do not mail me a copy of your followup]

"Alf P. Steinbach" <alf.p.stein...@gmail.com> spake the secret code
<p4q063$bi2$1...@dont-email.me> thusly:

>That said, I /think/ this code is valid:
>
> auto main() -> int

Valid, but ugly. :-)
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Terminals Wiki <http://terminals-wiki.org>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>

Öö Tiib

unread,
Jan 30, 2018, 12:50:57 PM1/30/18
to
On Tuesday, 30 January 2018 16:41:24 UTC+2, Alf P. Steinbach wrote:
> That said, I /think/ this code is valid:
>
> struct S
> {
> template< class Result >
> operator Result() const { return {}; }
> };
>
> auto main() -> int
> {
> S s;
> int i = s;
> double d = s;
> }
>
> Disclaimer: off the cuff code.

It seems valid by [temp.mem].

> Assuming the code is valid the open question is then, where does the
> standard allow function template instantiations to differ in return type
> only?

The conversion function is member function. Return value type and
template arguments are all part of member function template's
signature. By [defns.signature.member.templ]

> And (I don't think so) is there any way to refer to that function with
> explicit template parameter?

You must be able to by also [temp.mem]. Use:

int i = s.operator int();

... in your above example.
It is illegal to use your favorite syntax for conversion functions
like "operator auto() -> short;". ;)

Manfred

unread,
Jan 30, 2018, 3:02:33 PM1/30/18
to
On 1/30/2018 3:41 PM, Alf P. Steinbach wrote:
> On 1/30/2018 3:16 PM, Stefan Ram wrote:
>>    I have a version of GCC here that is not too old and does not
>>    give an error message for the code below.
>>
>> void f(){}
>> //int f();
>> int main() { int f();  }
>>
>>    It /does/ give an error message when »//« is removed, however.
>>    I also have a version of clang, giving an error message for the
>>    code above.
>
> Ordinary function overloads can't differ in return type only, so that
> lack of diagnostic is clearly a compiler bug.
>

I don't think it is a compiler bug - in fact this is not about function
overloading, it is an example of name hiding instead [*].

(on the other hand, when the '//' is removed, it would be function
overloading, hence the compiler error)

[*] See also n4618 3.4.2, sub (3.2)

Manfred

unread,
Jan 30, 2018, 3:43:04 PM1/30/18
to
On 1/30/2018 5:51 PM, James R. Kuyper wrote:
> On 01/30/2018 10:01 AM, James R. Kuyper wrote:
>> On 01/30/2018 09:16 AM, Stefan Ram wrote:
>>>     I have a version of GCC here that is not too old and does not
>>>     give an error message for the code below.
>>>
>>> void f(){}
>>
>> This declaration has file scope.
>>
>>> //int f();
>>
>> "Function declarations that differ only in the return type, the
>> exception specification (18.4), or both cannot be overloaded." (16.1p2).
>> "A program is ill-formed if it contains two such non-overloadable
>> declarations in the same scope." (16.1p1).
>>
>>> int main() { int f();  }
>>
>> This declaration has block scope, therefore 16.1p1 doesn't apply. Inside
>> the scope of this declaration, it hides the file scope declaration.
>> However, 16.1p2 should still apply - it isn't permitted for a function
>> matching this declaration to be defined in the same program as the first
>> definition of f() above. It seems to me that this should qualify as a
>> diagnosable rule, and therefore one that must result in a diagnostic
>> (4.1p2), but gcc apparently disagrees with me about that.

(you are probably referring to a different version of the standard than
n4618)
n4618 3.4p1 says: "Overload resolution(13.3) takes place after name
lookup has succeeded" so there appears to be a precedence of name hiding
(3.3.10) over overload resolution, or at least this is how I understand it.

>>
>> I modified f() to print some output, and modified main() to print the
>> value returned by f() - both printouts worked. The returned value was
>> 6295680.

Interesting experiment; the way I understand this is that this happens
because of 2 step program generation: /compilation/ succeeds (because of
the above), and then /linking/ succeeds too by finding 'void f()' as a
match for 'int f()', which is weird [*].
In fact the same result of yours happens if 'void f()' is moved to a
separate compilation unit (where there would be no question of overload
resolution).

[*] weird but still looks consistent with 1.3.19 - definition of
function signature:
1.3.19 [defns.signature]
signature
<function> name, parameter type list (8.3.5), and enclosing namespace
(if any)
[ Note: Signatures are used as a basis for name mangling and
linking.—end note ]

James R. Kuyper

unread,
Jan 30, 2018, 4:42:55 PM1/30/18
to
On 01/30/2018 03:42 PM, Manfred wrote:
> On 1/30/2018 5:51 PM, James R. Kuyper wrote:
>> On 01/30/2018 10:01 AM, James R. Kuyper wrote:
>>> On 01/30/2018 09:16 AM, Stefan Ram wrote:
>>>>     I have a version of GCC here that is not too old and does not
>>>>     give an error message for the code below.
>>>>
>>>> void f(){}
>>>
>>> This declaration has file scope.
>>>
>>>> //int f();
>>>
>>> "Function declarations that differ only in the return type, the
>>> exception specification (18.4), or both cannot be overloaded." (16.1p2).
>>> "A program is ill-formed if it contains two such non-overloadable
>>> declarations in the same scope." (16.1p1).
>>>
>>>> int main() { int f();  }
>>>
>>> This declaration has block scope, therefore 16.1p1 doesn't apply. Inside
>>> the scope of this declaration, it hides the file scope declaration.
>>> However, 16.1p2 should still apply - it isn't permitted for a function
>>> matching this declaration to be defined in the same program as the first
>>> definition of f() above. It seems to me that this should qualify as a
>>> diagnosable rule, and therefore one that must result in a diagnostic
>>> (4.1p2), but gcc apparently disagrees with me about that.
>
> (you are probably referring to a different version of the standard than
> n4618)

I'm using n4659.pdf, dated 2-17-03-21.

> n4618 3.4p1 says: "Overload resolution(13.3) takes place after name
> lookup has succeeded"

That's 6.4p1 in n4659.pdf, and the cross-reference is to 16.3 rather
than 13.3. The words are the same, however.


> ... so there appears to be a precedence of name hiding
> (3.3.10) over overload resolution, or at least this is how I understand it.

Overload resolution doesn't come into play in this version of the code,
since it contains no calls to any function. My version down below does
call f(), and name hiding does determine that it should use the
declaration of f() with a return type of 'int', so overload resolution
still ends up with no role to play in this code. However, this code
contains no definition for any function that matches that declaration.

The very existence of overloads that differ only by their return type is
prohibited by 16.1p2, regardless of hiding or overload resolution.
Therefore, it is not permitted for there to be any actual function named
f() which is compatible with the block-scope declaration of f().

>>> I modified f() to print some output, and modified main() to print the
>>> value returned by f() - both printouts worked. The returned value was
>>> 6295680.
>
> Interesting experiment; the way I understand this is that this happens
> because of 2 step program generation: /compilation/ succeeds (because of
> the above), and then /linking/ succeeds too by finding 'void f()' as a
> match for 'int f()', which is weird [*].
> In fact the same result of yours happens if 'void f()' is moved to a
> separate compilation unit (where there would be no question of overload
> resolution).

The key point is
"Two names that are the same (Clause 6) and that are declared in
different scopes shall denote the same variable, function, type,
template or namespace if
— both names have external linkage or ..." (6.5p9)

The definition of f() and the block scope declaration of f() are both
declarations of the same name, which has external linkage.

and "... the types specified by all declarations referring to a given
variable or function shall be identical... A violation of this rule on
type identity does not require a diagnostic." (6.5p10)

...

Richard

unread,
Jan 30, 2018, 5:33:39 PM1/30/18
to
[Please do not mail me a copy of your followup]

Manfred <non...@invalid.add> spake the secret code
<p4qlc9$1sec$1...@gioia.aioe.org> thusly:

>(you are probably referring to a different version of the standard than
>n4618)

I thought n4659 was the last published draft before C++17 was accepted.

<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf>

Manfred

unread,
Jan 31, 2018, 8:36:12 AM1/31/18
to
On 1/30/2018 11:33 PM, Richard wrote:
> [Please do not mail me a copy of your followup]
>
> Manfred <non...@invalid.add> spake the secret code
> <p4qlc9$1sec$1...@gioia.aioe.org> thusly:
>
>> (you are probably referring to a different version of the standard than
>> n4618)
>
> I thought n4659 was the last published draft before C++17 was accepted.
Probably so, my note was only about the different section numbering.
The content is not different on this matter, though. (and I believe the
committee is not likely to change such basic features).

>
> <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf>
>

Manfred

unread,
Jan 31, 2018, 9:10:06 AM1/31/18
to
I am referring to n4659 too in this follow-up.

>
>
>> ... so there appears to be a precedence of name hiding
>> (3.3.10) over overload resolution, or at least this is how I
>> understand it.
>
> Overload resolution doesn't come into play in this version of the code,
> since it contains no calls to any function. My version down below does
> call f(), and name hiding does determine that it should use the
> declaration of f() with a return type of 'int', so overload resolution
> still ends up with no role to play in this code. However, this code
> contains no definition for any function that matches that declaration.
I would correct this last part, in that the code /does/ contain a
definition of a function that matches the function /signature/ of f() -
the key issue being that the function signature does not contain the
function return type.

>
> The very existence of overloads that differ only by their return type is
> prohibited by 16.1p2, regardless of hiding or overload resolution.
> Therefore, it is not permitted for there to be any actual function named
> f() which is compatible with the block-scope declaration of f().
This is not how I would understand it, since hidden names would not
participate in overload resolution (because of 6.4 p1).

>
>>>> I modified f() to print some output, and modified main() to print the
>>>> value returned by f() - both printouts worked. The returned value was
>>>> 6295680.
>>
>> Interesting experiment; the way I understand this is that this happens
>> because of 2 step program generation: /compilation/ succeeds (because of
>> the above), and then /linking/ succeeds too by finding 'void f()' as a
>> match for 'int f()', which is weird [*].
>> In fact the same result of yours happens if 'void f()' is moved to a
>> separate compilation unit (where there would be no question of overload
>> resolution).
>
> The key point is
> "Two names that are the same (Clause 6) and that are declared in
> different scopes shall denote the same variable, function, type,
> template or namespace if
>   — both names have external linkage or ..." (6.5p9)
>
> The definition of f() and the block scope declaration of f() are both
> declarations of the same name, which has external linkage.
>
> and "... the types specified by all declarations referring to a given
> variable or function shall be identical... A violation of this rule on
> type identity does not require a diagnostic." (6.5p10)
I believe you are right on this, which is in fact a requirement about
linkage.
p9 states that both declarations of f() "shall denote the same function"
and p9.1, 9.2 and 9.3 define this identity based on linkage, namespace
and parameter list identity (i.e. function /signature/)
The following p10 states that the /types/ shall be identical (which is
not the same as signatures, singe the function return type is part of
the function type, but not part of the function signature), but no
diagnostic is required from the compiler in case of violation.

To my understanding, this sums up to the following:
- The program is ill-formed (because it violates 6.5 p10)
- But the compiler is still compliant since "a diagnostic is not required".

This apparent inconsistency is explained by the linking rules (which are
based on signature instead of type), and is part of the inheritance of C.

<snip>

Richard

unread,
Jan 31, 2018, 11:44:25 AM1/31/18
to
[Please do not mail me a copy of your followup]

Manfred <non...@invalid.add> spake the secret code
<p4sgnp$n23$1...@gioia.aioe.org> thusly:

>On 1/30/2018 11:33 PM, Richard wrote:
>> [Please do not mail me a copy of your followup]
>>
>> Manfred <non...@invalid.add> spake the secret code
>> <p4qlc9$1sec$1...@gioia.aioe.org> thusly:
>>
>>> (you are probably referring to a different version of the standard than
>>> n4618)
>>
>> I thought n4659 was the last published draft before C++17 was accepted.
>Probably so, my note was only about the different section numbering.
>The content is not different on this matter, though. (and I believe the
>committee is not likely to change such basic features).

If it helps, I've put links to what I believe are the last published
draft standards for C++11, C++14 and C++17 in the Reference sidebar
on my user group website:

Utah C++ Programmers <http://utahcpp.wordpress.com>

I got sick of trying to find which draft was the last published before
acceptance of the standard :).

James R. Kuyper

unread,
Jan 31, 2018, 2:09:21 PM1/31/18
to
...
>>> ... so there appears to be a precedence of name hiding
>>> (3.3.10) over overload resolution, or at least this is how I
>>> understand it.
>>
>> Overload resolution doesn't come into play in this version of the code,
>> since it contains no calls to any function. My version down below does
>> call f(), and name hiding does determine that it should use the
>> declaration of f() with a return type of 'int', so overload resolution
>> still ends up with no role to play in this code. However, this code
>> contains no definition for any function that matches that declaration.
> I would correct this last part, in that the code /does/ contain a
> definition of a function that matches the function /signature/ of f() -
> the key issue being that the function signature does not contain the
> function return type.

6.5p9 requires that the signatures match, only for determining whether
the two declarations identify the same function. 6.5p10 requires
declarations identifying the same function must have the same type, and
that's the "matching" that I was referring to that can't happen in this
case.

>> The very existence of overloads that differ only by their return type is
>> prohibited by 16.1p2, regardless of hiding or overload resolution.
>> Therefore, it is not permitted for there to be any actual function named
>> f() which is compatible with the block-scope declaration of f().
> This is not how I would understand it, since hidden names would not
> participate in overload resolution (because of 6.4 p1).

16.1p2 does not require that the issue ever come up as to which of the
two overloads should be used - it simply says that they should not both
exist.

...
>> The key point is
>> "Two names that are the same (Clause 6) and that are declared in
>> different scopes shall denote the same variable, function, type,
>> template or namespace if
>>   — both names have external linkage or ..." (6.5p9)
>>
>> The definition of f() and the block scope declaration of f() are both
>> declarations of the same name, which has external linkage.
>>
>> and "... the types specified by all declarations referring to a given
>> variable or function shall be identical... A violation of this rule on
>> type identity does not require a diagnostic." (6.5p10)
> I believe you are right on this, which is in fact a requirement about
> linkage.
> p9 states that both declarations of f() "shall denote the same function"
> and p9.1, 9.2 and 9.3 define this identity based on linkage, namespace
> and parameter list identity (i.e. function /signature/)

I noticed the "or" in 6.5p9.1, but didn't notice that p9.1, p9.2, p9.3
and p9.4 are all connected to each other by "and" - but that doesn't
change the results. Both declarations are in the same namespace, and
denote functions with the same parameter type lists, so 6.5p10
definitely applies, and definitely prohibits them from having different
types.

...
> To my understanding, this sums up to the following:
> - The program is ill-formed (because it violates 6.5 p10)
> - But the compiler is still compliant since "a diagnostic is not required".

Agreed.
0 new messages