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;
}
OverShifted
  • 457
  • 1
  • 7
  • 17
Marco A.
  • 43,032
  • 26
  • 132
  • 246

1 Answers1

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.

iBug
  • 35,554
  • 7
  • 89
  • 134
Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • Thanks, shouldn't the argument be "Timer (*) ()" ? Is "Timer ()" an acceptable function pointer too? – Marco A. Jun 12 '13 at 08:21
  • 1
    I guess, that's the case of `functionName` and `&functionName` being the same thing. – Spook Jun 12 '13 at 08:22
  • @DavidKernin: Functions automatically decay to pointers when used as function arguments, so `void(Timer())` and `void(Timer(*)())` are identical types. – Andy Prowl Jun 12 '13 at 08:26
  • To be honest the assertion is thrown with MSVC2012, but I think it's a compiler-related bug. – Marco A. Jun 12 '13 at 08:49
  • @DavidKernin: I believe so. Sounds like an interesting new question for SO :) – Andy Prowl Jun 12 '13 at 08:51
  • For reference this ambiguity resolution is described in the C++ standard 8.2.1/1 Declarators / Ambiguity Resolution [dcl.ambig.res] – Andrew Tomazos Jun 12 '13 at 09:05
  • @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(*)())`. – Zach Saw Feb 06 '14 at 03:10
  • @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. – Andy Prowl Feb 06 '14 at 09:27
  • @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. – Zach Saw Feb 07 '14 at 00:54
  • @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`. 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). – Andy Prowl Feb 07 '14 at 09:07
  • @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? – Zach Saw Feb 07 '14 at 10:16
  • @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. – Andy Prowl Feb 07 '14 at 13:00
  • so I'm confused why either C or C++ allows Timer() as a synonym for Timer(*)() in the parameters... – dennis97519 May 17 '20 at 01:52