Strange code that compiles with g++

C++GccC++11G++

C++ Problem Overview


The following code compiles successfully with g++ 4.8.1:

int main()
{
    int(*)();
}

It looks like a simple declaration of a pointer to function:

int(*f)();

It doesn't compile with clang 3.4 and vc++ 2013.

Is it a compiler bug or one of dark places of the standard?


List of similar strange code pieces which compile fine with g++ 4.8.1 (updated):
  1. int(*)();

  2. int(*);

  3. int(*){};

  4. int(*());

Live example with these strange code pieces.

Update 1: @Ali added some interesting information in the comments:

> All 4 cases give a compile error with clang 3.5 trunk (202594) and compile fine with gcc 4.9 trunk (20140302). The behavior is the same with -std=c++98 -pedantic, except for int(*){}; which is understandable; extended initializer lists only available with -std=c++11.

Update 2: As @CantChooseUsernames noted in his answer they still compile fine even with initialization and no assembly is generated for them by g++ (neither with nor without initialization) even without any enabled optimization:

  1. int(*)() = 0;

  2. int(*) = 0;

  3. int(*){} = 0;

  4. int(*()) = 0;

Live example with initializations.

Update 3: I was really surprised to find that int(*)() = "Hello, world!"; compiles fine, too (while int(*p)() = "Hello, world!"; doesn't compile, of course).

Update 4: It is fantastic but int(*){} = Hello, world!; compiles fine. And the following extremely strange piece of code, too: int(*){}() = -+*/%&|^~.,:!?$()[]{}; (live example).

Update 5: As @zwol noted in his comment

> This and a number of related syntactic problems are being tracked as gcc bug 68265.

C++ Solutions


Solution 1 - C++

According to the C++ Standard (p. #6 of section 7 Declarations)

> 6 Each init-declarator in the init-declarator-list contains exactly > one declarator-id, which is the name declared by that > init-declarator and hence one of the names declared by the declaration

So it is simply a compiler bug.

The valid code could look as for example (apart from the function pointer declaration showed by you) though I can not compile it with my MS VC++ 2010.

int(*p){};

It seems that the compiler you are using for testing allows declarations without a declarator-id.

Also take into account the following paragraph of section 8.1 Type names

> 1 To specify type conversions explicitly, and as an argument of > sizeof, alignof, new, or typeid, the name of a type shall be > specified. This can be done with a type-id, which is syntactically a > declaration for a variable or function of that type that omits the > name of the entity.

Solution 2 - C++

I am not sure how much this helps, but I tried the following (clang 3.3, g++ 4.8.1):

using P = int(*)();
using Q = int*;
P; // warning only
Q; // warning only
int(*)(); // error (but only in clang)
int*;     // error
int(*p)(); // ok
int *q;    // ok

On the other hand, everything compiles fine in g++ 4.8.2 and 4.9.0. I have no clang 3.4, unfortunately.

Very roughly, a declaration [iso section 7] consists of the following parts in order:

  1. optional prefix specifiers (e.g. static, virtual)
  2. base type (e.g. const double, vector<int>)
  3. declarator (e.g. n, *p, a[7], f(int))
  4. optional suffix function specifiers (e.g. const, noexcept)
  5. optional initializer or function body (e.g. = {1,2,3} or { return 0; }

Now, a declarator roughly consists of a name and optionally some declarator operators [iso 8/4].

Prefix operators, e.g.:

  • * (pointer)
  • *const (constant pointer)
  • & (lvalue reference)
  • && (rvalue reference)
  • auto (function return type, when trailing)

Postfix operators, e.g.:

  • [] (array)
  • () (function)
  • -> (function trailing return type)

The above operators were designed to reflect their use in expressions. Postfix operators bind tighter than prefix, and parentheses can be used to change their order: int *f() is a function returning a pointer to int, whereas int (*f)() is a pointer to a function returning int.

Maybe I am wrong, but I think these operators cannot be in the declaration without the name. So when we write int *q;, then int is the base type, and *q is the declarator consisting of prefix operator * followed by name q. But int *; cannot appear by itself.

On the other hand, when we define using Q = int*;, then declaration Q; is fine by itself because Q is the base type. Of course, because we are not declaring anything, we may get an error or a warning depending on compiler options, but this is a different error.

The above are just my understanding. What the standard (e.g. N3337) says is [iso 8.3/1]:

> Each declarator contains exactly one declarator-id; it names the identifier that is declared. An unqualified-id occurring in a declarator-id shall be a simple identifier except for the declaration of some special functions (12.3 [user-defined conversions], 12.4 [destructors], 13.5 [overloaded operators]) and for the declaration of template specializations or partial specializations (14.7).

(notes in square brackets are mine). So I understand int(*)(); should be invalid and I cannot say why it has different behaviour in clang and different versions of g++.

Solution 3 - C++

You can use this: http://gcc.godbolt.org/ to view the assembly..

int main()
{
    int(*)() = 0;
    return 0;
}

Generates:

main:
	pushq	%rbp
	movq	%rsp, %rbp
	movl	$0, %eax
	popq	%rbp
	ret

Which is equivalent to: int main() {return 0;} So even with NO optimization, gcc just doesn't generate assembly for it.. Should it give a warning or error? I have no clue but it doesn't care or do anything for the unnamed func pointer.

However:

int main()
{
    int (*p)() = 0;
    return 0;
}

With no optimization will generate:

main:
	pushq	%rbp
	movq	%rsp, %rbp
	movq	$0, -8(%rbp)
	movl	$0, %eax
	popq	%rbp
	ret

which allocates 8 bytes on the stack..

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionConstructorView Question on Stackoverflow
Solution 1 - C++Vlad from MoscowView Answer on Stackoverflow
Solution 2 - C++iavrView Answer on Stackoverflow
Solution 3 - C++BrandonView Answer on Stackoverflow