9

I'm putting together a C++-based assignment for a class I'm teaching. I have a function I'm exporting to students that I'd like them to call at various points in their program so that, during grading, we can intercept those calls to make sure that they're doing the right things at the right times. I don't want that code to do anything in the provided starter files, so I just gave the function a body that just has a series of statements that casts all the arguments to void to suppress compiler warnings about unused arguments. In the course of doing so, I ran into an unusual compiler error I've never seen before, and a search over this site didn't turn up anything helpful.

The error can be best exemplified by this reduced test case:

void iDontUseMyArguments(int a, int b) {
    (void) a;    // Explicit cast to void - totally fine!
    (void) b;
}

void iDontEither(int a, int b) {
    (void) a, b;  // Comma expression casted to void, though technically
                  // b isn't casted to void!
}

void norDoI(int a, int b) {
    void(a, b);   // ERROR! No idea why this isn't okay.
}

void meNeither(int a, int b) {
    (void)(a, b); // Comma expression casted to void - totally fine!
}

void jumpOnBandwagon(int a, int b) {
    void((a, b)); // Comma expression casted to void - totally fine!
}

As you can see, most of these compile just fine. The issue was in this one:

void(a, b);

This triggers the following error:

prog.cpp: In function 'void norDoI(int, int)':
prog.cpp:11:11: error: expression list treated as compound expression in functional cast [-fpermissive]
   void(a, b);
           ^

I've never encountered this error message, so I'm not sure what this is trying to tell me.

The intent behind the line

void(a, b);

was to be a comma expression involving a and b that was then casted to type void using a function-style cast. As you can see, the following variants all work:

(void)(a, b);
void((a, b));

I suspect this likely has something to do with the Most Vexing Parse and this getting interpreted as a declaration, but the particular error I'm getting doesn't seem to match this.

My question is the following:

  1. Why, exactly, isn't this code legal?
  2. What does the compiler think I'm trying to do?
  3. Why, exactly, are these other variants legal?
templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • 1
    I'd say, you have to learn a lot more before you start teaching! – SergeyA Jan 19 '17 at 18:48
  • 3
    @SergeyA What a mean thing to say. Who says they teach C++. – NathanOliver Jan 19 '17 at 18:50
  • @NathanOliver, I am sure OP teaches C++. If I am wrong, I beg my pardon. If I am correct, this would signify all the problems with programming education we have in our schools. – SergeyA Jan 19 '17 at 18:51
  • 11
    @SergeyA I completely disagree. A good teacher is not a perfect god-like C++ super guru who knows every dark corner of the language. A solid knowledge of the language is required, but not knowing things is OK as long as you as a teacher are ready to learn. Op clearly has this. Also being able to pass the knowledge and inspire people is an extremely important part of teaching. – bolov Jan 19 '17 at 19:02
  • 1
    "a perfect god-like C++ super guru who knows every dark corner of the language" - oh, you mean those 0.001% of C++ programmers who (among other things) think everybody should read the standard? :D – Karoly Horvath Jan 19 '17 at 19:06
  • 2
    Read the standard. It was crap. I couldn't get an emotional attachment to any of the characters. Not even '\t', and you'd figure if anyone had an interesting story it would be Tab. – user4581301 Jan 19 '17 at 19:28
  • 1
    for those who have not read the standard yet, I am going to spoil the ending: in the *future* `uses_allocator` ends up requiring an `Allocator`. Quite anticlimactic if you ask me... – bolov Jan 19 '17 at 19:46

3 Answers3

8

Given this structure:

struct S {
  S(int, int) {}
};

What does S(1, 2) mean?

Answer: it means you construct an object of type S from the values 1 and 2.


Given this function template:

template <typename T> T f() { return T(1, 2); }

What does T(1, 2) mean?

Answer: it means you construct an object of type T from the values 1 and 2, which is possible if T is for instance the type S from the first question.


Given that same function template, what then does f<void>() mean?

Answer: it attempts to construct a single void value from the two values 1 and 2. This fails because only single values can be converted to void.


Does void(1, 2) mean anything different from T(1, 2) when T happens to be void?

Answer: no, it means exactly the same thing, which is why it's an error.

  • That makes perfect sense. So the reason why `void((1, 2))` works is that it can't be parsed as an argument list and has to be parsed as a use of the comma operator? – templatetypedef Jan 19 '17 at 21:45
  • @templatetypedef Pretty much. The standard's grammar describes it a little bit differently, but really, it amounts to pretty much the same thing as your comment. –  Jan 19 '17 at 22:20
3

Looking at documentation I suppose that you have violated one of conversion cases rule (#3):

If there are more than one expression in parentheses, new_type must be a class with a suitably declared constructor. This expression is a prvalue of type new_type designating a temporary (until C++17)whose result object is (since C++17) direct-initialized with expressions.

sebap123
  • 2,541
  • 6
  • 45
  • 81
0

In the line void(a, b); compiler "think" that you are trying to use a type as function name, so it gives an error.

Other cases are type casting...

Rama
  • 3,222
  • 2
  • 11
  • 26
  • Just by the message given by the compiler. http://stackoverflow.com/questions/4775781/what-is-the-difference-between-c-like-casting-functional-casting – Rama Jan 19 '17 at 18:57
  • I don't believe this is the root cause of the problem, since you can use type names in contexts like this one. Thanks, though! – templatetypedef Jan 19 '17 at 22:10