Most vexing parse confusion
Asked Answered
D

1

8

I'm studying C++11 and I stumbled upon uniform initializers.

I don't understand the following code which should show the "most vexing parse" ambiguity:

#include<iostream>
 
class Timer
{
public:
    Timer() {}
};

int main() 
{
    auto dv = Timer(); // What is Timer() ? And what type is dv?
    int time_keeper(Timer()); // This is a function right? And why isn't the argument " Timer (*) ()" ?
    return 0;
}
Derinna answered 12/6, 2013 at 8:15 Comment(2)
AFAIK the second line invokes the MVP, the first one doesn't.Wardell
I know that, but I'm confused with the types involved in the codeDerinna
F
12

Here:

auto dv = Timer();

You have an object of type Timer called dv that is being copy-initialized from a temporary (the expression on the right side of the = sign).

When using auto to declare a variable, the type of that variable is the same as the type of the expression that initializes it - not considering cv-qualifiers and references here.

In your case, the expression that initializes dv has type Timer, and so dv has type Timer.

Here:

int time_keeper(Timer());

You declare a function called time_keeper that returns an int and takes as its input a pointer to a function which returns a Timer and takes no argument.

And why isn't the argument Timer (*) () ?

Functions decay to pointers when passed as an argument, so the type of time_keeper is actually int(Timer(*)()).

To convince yourself, you could try compiling this little program:

#include <type_traits>

struct Timer { };
int main()
{
    int time_keeper(Timer());
    static_assert(
        std::is_same<
            decltype(time_keeper), 
            int(Timer(*)())
        >::value, 
        "This should not fire!");
}

Here is a live example.

Framing answered 12/6, 2013 at 8:18 Comment(13)
Thanks, shouldn't the argument be "Timer (*) ()" ? Is "Timer ()" an acceptable function pointer too?Derinna
I guess, that's the case of functionName and &functionName being the same thing.Slacker
@DavidKernin: Functions automatically decay to pointers when used as function arguments, so void(Timer()) and void(Timer(*)()) are identical types.Framing
To be honest the assertion is thrown with MSVC2012, but I think it's a compiler-related bug.Derinna
@DavidKernin: I believe so. Sounds like an interesting new question for SO :)Framing
For reference this ambiguity resolution is described in the C++ standard 8.2.1/1 Declarators / Ambiguity Resolution [dcl.ambig.res]Caernarvonshire
@AndyProwl: I know the std says this but why allow functions to decay to pointers in function args? It's not exactly intuitive to specify void(Timer()) to mean void(Timer(*)()).Monto
@ZachSaw: Functions cannot be manipulated as values, only as references or pointers. Think about it, what would you do inside your function with an argument of function type, other than calling it (which is something you can do even if your argument is a pointer or reference to function)? So when you see Ret(Args...) (like Timer() in this case, where Args... is empty) to mean a function's parameter type, the compiler automatically assumes you mean a pointer to such a function. Why not a reference? Well, I guess that's because it comes from C, where references don't exist.Framing
@AndyProwl: I actually meant void(Timer()) should be a compile error, rather than the compiler implicitly decaying it into a pointer. In which case, int time_keeper(Timer()); would no longer trigger the most vexing parse problem.Monto
@ZachSaw: OK, so your point is that Timer() should lead to an error when it is used as the type of an argument in a function, but be accepted outside of that context - as in std::function<Timer()>. I'm not a compiler expert so I can't really tell whether this "asymmetry" is problematic or not, anyway I'm quite sure the reason why this thing exists in C++ is because of backwards compatibility with C (where there is no such ambiguity, because C does not have constructors).Framing
@AndyProwl A C compiler is so very different from a C++ compiler. I never understood the need for C++ to be backwards compatible with C. Is it still true that all C codes can be compiled with a C++-only compiler?Monto
@ZachSaw: There are a few breaking changes in C++, and a few "newer" features of C (C99 or C11) which never became part of C++ (e.g. variable-length arrays), but for the most of it, yes. Take it with a grain of salt though, I'm not a C expert either.Framing
so I'm confused why either C or C++ allows Timer() as a synonym for Timer(*)() in the parameters...Weissmann

© 2022 - 2024 — McMap. All rights reserved.