Using 'void' template arguments in C++

Nick Hutchinson picture Nick Hutchinson · Nov 14, 2012 · Viewed 35.5k times · Source

Take the following minimal example:

using Type1 = std::function<void(void)>;

template <typename T>
using Type2 = std::function<void(T)>;

Type1 whyDoesThisWork;
Type2<void> andYetThisDoesNot;

If the second type alias, I get the error "Argument may not have 'void' type". (I tested with Xcode 4.5, Clang/c++11/libc++, OS X 10.7.)

I find this curious: I would have expected Type1 and Type2<void> to behave identically. What's going on here? And is there a way to rewrite the second type alias so I can write Type2<void> and get std::function<void(void)> instead of an error?

Edit I should probably add that the reason I want this is to allow for something like the following:

template <typename ... T>
using Continuation = std::function<void(T...)>;

auto someFunc = []() -> void {
  printf("I'm returning void!\n");
};

Continuation<decltype(someFunc())> c;

Continuation<decltype(someFunc())> becomes Continuation<void> and I get the error.

Answer

Kerrek SB picture Kerrek SB · Nov 14, 2012

I don't have an actual answer, only what I said in the comment: You can't have void as a function type, as in:

int foo(int, char, void, bool, void, void);     // nonsense!

I believe that T(void) is only allowed as a compatibility notation for C (which distinguishes declarations and prototypes, very differently from C++, and which needs to be able to say "no arguments").

So, the solution should be variadic:

template <typename ...Args> using myType = std::function<void(Args...)>;

That way you can properly have no arguments:

myType<> f = []() { std::cout << "Boo\n"; }