20

I don't understand why the following code fails to compile when using constructor-style casting:

template<typename T> void foo(const T& t){}

int main(){
  foo(unsigned char(0));
}

The errors are:

  • error: expected primary-expression before ‘unsigned’ for gcc.
  • error: expected '(' for function-style cast or type construction for clang

However these three syntaxes are correct:

template<typename T> void foo(const T& t){}

int main(){
  // c-style cast
  foo((unsigned char)0);

  // without unsigned
  foo(char(0));

  // aliased unsigned char
  typedef unsigned char uchar;
  foo(uchar(0));
}

So the space in the type is obviously to blame here.

I thought it might be somehow related to our old friend the most vexing parse, so I tried the uniform initialization syntax, which is supposed to get rid of this sort of ambiguities, but no luck:

template<typename T> void foo(const T& t){}

int main(){
  foo(unsigned char{0});
}

But still:

  • error: expected primary-expression before ‘unsigned’ for gcc.
  • error: expected '(' for function-style cast or type construction for clang

So my question is why is it not allowed to have a type containing a space in function-style casts? It doesn't look ambiguous to me.

note: I know I can write foo<unsigned char>(0), but it doesn't answer the question ;)

Thibaut
  • 2,400
  • 1
  • 16
  • 28
  • 2
    For what it's worth `int main() { unsigned char c = unsigned char(0); } ` doesn't compile either using g++. – R Sahu Apr 21 '14 at 17:32
  • Please excuse my ignorance—but is not ``(unsigned char)(0)`` what the OP wants? Or does it have different semantics? (Simple yes/no answer is sufficient, I’ll dig into it deeper when you tell me it’s different by myself) – Jonas Schäfer Apr 21 '14 at 17:38
  • Also, both the constructs are successfully compiled by Visual Studio 2008. – R Sahu Apr 21 '14 at 17:38
  • @JonasWielicki Yours is a C-style casting. They are equivalent, but I was wondering if that was not a compiler bug, which would be bad. There are multiple semantically equivalent syntax, but I don't know why some are not valid. – Thibaut Apr 21 '14 at 17:40
  • @JonasWielicki: The question is about _functional notation_ specifically, which that is not. – Lightness Races in Orbit Apr 21 '14 at 17:42
  • 2
    I'm retracting my close vote; this _is_ a duplicate of http://stackoverflow.com/questions/2963396/gcc-doesnt-like-c-style-casts-with-spaces, but that question doesn't look as good and the answers sort of skirt around the issue. I'm going to take the unusual step of voting _that_ one as a dup of _this_ one. – Lightness Races in Orbit Apr 21 '14 at 17:46
  • The problem is that someone somehow thought clever to introduce blanks in type names at some point in C (`signed` and `unsigned` with various combinations of `char`, `short`, `int`, `long` and `long long`). It was a bad idea, and now we live with it... – Matthieu M. Apr 21 '14 at 17:53

1 Answers1

16

[C++11: 5.2.3/1]: A simple-type-specifier (7.1.6.2) or typename-specifier (14.6) followed by a parenthesized expression-list constructs a value of the specified type given the expression list. [..]

Examining the grammar, we see that the only way to get unsigned char from the simple-type-specifier production is by concatenating two of them.

As evidence debunking the rumour that table 10 is stating the contrary, which I may myself have started a short while ago (:P), the table heading says "specifier(s)" (note the optional plural), and refer to the below passage:

[C++11: 5.2.3/2]: [..] Table 10 summarizes the valid combinations of simple-type-specifiers and the types they specify. (emphasis mine)

Now, combining simple-type-specifiers is allowed in some cases:

[C++11: 7.1.6.2/3]: When multiple simple-type-specifiers are allowed, they can be freely intermixed with other decl-specifiers in any order. [..]

… but there's no indication that this is the case with functional notation, which clearly states "a simple-type-specifier" — singular.

Therefore GCC is correct, and Visual Studio is wrong.

As for why this is the case... well, I don't know. I suspect we could come up with some ambiguous edge case, but Casey makes a good point in the comments below that allowing this would be inconsistent with function call syntax, since names of functions cannot have spaces in them.

Community
  • 1
  • 1
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • I thought it might be a compiler bug too (especially the uniform initialization syntax), but from both clang and gcc that seems unlikely. Thanks for the pointers to the standard, I will check that out. – Thibaut Apr 21 '14 at 17:36
  • @Thibaut: I'm not aware of any compiler that accepts `unsigned char` with functional notation (though I haven't tried VS); but having looked into it from the standard's POV for the first time just now, I honestly can't explain _why_ that is. – Lightness Races in Orbit Apr 21 '14 at 17:38
  • a comment to your linked post, is uniform initialization considered a type of casting? I thought it was just a strongly typed initialization. – Thibaut Apr 21 '14 at 17:38
  • 1
    @Thibaut VS2013 is compiling your code without any complaints. That's gotta be a first, where VS gets something right while clang and gcc don't. I think this answer is correct, because *Table 10* under *[dcl.type.simple]* lists `unsigned char` as a *simple-type-specifier*. – Praetorian Apr 21 '14 at 17:40
  • `[C++11: 5.2.3/3]:` Similarly, a _simple-type-specifier_ or _typename-specifier_ followed by a _braced-init-list_ creates a temporary object of the specified type _direct-list-initialized_ (8.5.4) with the specified _braced-init-list_, and its value is that temporary object as a prvalue. _[<-- also under the section of functional notation explicit conversions so, yes.]_ – Lightness Races in Orbit Apr 21 '14 at 17:40
  • @LightnessRacesinOrbit Praetorian is right, `unsigned char` is explicitly a `simple-type-specifier` (7.1.6.2), which is a valid functional casting notation (5.2.3.1), so this starts to look more and more like a compiler bug – Thibaut Apr 21 '14 at 17:45
  • 1
    @Thibaut: From the grammar, `unsigned char` looks more like multiple `simple-type-specifiers` "concatenated", which is at odds with Table 10. – Lightness Races in Orbit Apr 21 '14 at 17:47
  • 1
    Yeah, following on from my previous comment, I've changed my mind about what's going on here - GCC is right here. Table 10 is not at odds with anything if you read it properly ;) – Lightness Races in Orbit Apr 21 '14 at 17:54
  • The grammar rule for *type-name* is *type_name*: *class-name* | *enum-name* | *typedef-name* | *simple-template-id*. (C++11 7.1.6.2/1). `unsigned char` can't match any of those productions. – Casey Apr 21 '14 at 17:54
  • @Casey: Indeed - a single _simple-type-specifier_ cannot result in `unsigned char`. – Lightness Races in Orbit Apr 21 '14 at 17:57
  • It looks like we all agree that this syntax is explicitly forbidden by the grammar then. Although the wording is not as clear as it could be. – Thibaut Apr 21 '14 at 17:58
  • 4
    Well, at least VS is back to being wrong. Everything's right with the world once more. – Lightness Races in Orbit Apr 21 '14 at 17:58
  • 1
    @Thibaut Grammatically, it makes sense. We can't have function names with spaces in them, so it's consistent to forbid multi-word function-style casts. – Casey Apr 21 '14 at 18:01
  • I've stolen it for the answer :P – Lightness Races in Orbit Apr 21 '14 at 18:03
  • Thanks a lot LightnessRacesinOrbit and @Casey. You rock. – Thibaut Apr 21 '14 at 18:06