1

I want to create a template alias for the std::function

template <typename T, typename R>
using Func = std::function<R(T)>;

Next, initialize two variables using this alias:

Func<int, void> f = [](int x) { };  // ok
Func<void, int> g = []() { return 1; };  // error

Unfortunately, the second line rises the following error:

In substitution of 
‘template<class T, class R> 
 using Func = std::function<R(T)> [with T = void; R = int]’: 
error: invalid parameter type ‘void’

From my point of view, the line causing the error must be equivalent to

std::function<int(void)> h = [](){ return 1; };

which, of course, compiles fine.

Can somebody explain what's going on here, please?

Edit

Unfortunately, I haven't found a clear answer in related questions, including this one. After reading these answers, it is still unclear for me what is the origin of the problem with the code above. I am looking for a simple concise answer, perfectly, backed up by the references to the language specification.

frumle
  • 593
  • 5
  • 15
  • 2
    Does this answer your question? [Using 'void' template arguments in C++](https://stackoverflow.com/questions/13372173/using-void-template-arguments-in-c) Also https://stackoverflow.com/a/72391546/3370124 – Richard Critten Nov 10 '22 at 20:41
  • 1
    `template using Func = std::function;` then use `Func` and `Func` respectively. – Patrick Roberts Nov 10 '22 at 21:00
  • @Patrick Roberts, thanks, it does exactly what I want. However, I want to understand why my original code does not compile. What is special with the void type? Where I can read about this behavior in language specs? Thank you! – frumle Nov 10 '22 at 21:05
  • @frumle the thread you linked to already contains an answer quoting the relevant specification: https://stackoverflow.com/a/13372928 – Patrick Roberts Nov 10 '22 at 21:18
  • 1
    @frumle: it's a special feature of the void *keyword* that when it appears in parens by itself as an argument list, it has a special meaning. This is specific to just the void *keyword*, not the void type, and other types that are equivalent to void do not have this meaning. There are similar features with other keywords like `int` and `unsigned` that allows them to be combined into a type, which cannot be done with other names that are typedef's to be int or unsigned. – Chris Dodd Nov 11 '22 at 02:18

1 Answers1

2

The problem is that int(void) is not a type of a function taking a void parameter.
It is the same as int(), which is a function taking no parameters.
However, when you use an alias of void and not void itself, you get a type of a function taking a void parameter.
A parameter cannot have the type void and thus you get the error.

Daniel
  • 30,896
  • 18
  • 85
  • 139
  • Thank you. This is why compiler understands both the `std::function` and `std::function`. But why does not it just treat a "type of a function taking a void parameter" as a "function taking no parameters" when the aliases involved? – frumle Nov 10 '22 at 21:18
  • 1
    @frumle Letting `int(T)` to be the same as `int()` if `T` is void creates a lot of weird consequences: It means that `std::tuple` is the same as `std::tuple<>` (in order for `std::apply` to work), which in turn means that `std::tuple` be the same as `std::tuple` (due to `tuple_cat`), which means that you have `int(void, int, void)` being the same as `int(int)`, and `std::tuple_size>` not always being 2 (in case `T1` or `T2` or both are void)... Be careful when you tug on that loose string. The entire sweater may unravel. – Raymond Chen Nov 10 '22 at 21:56
  • @Raymond Chen The source of confusion in my case is that I don't understand what exactly such constructions as `int(void)`, or `int(int)` denote. On the one hand, they can appear in templates where `typename` is expected, so I may conclude that they denote type. On the other hand, I can not declare variable using this "type" (like `int(int) f = ...;`). I am completely lost at this point. – frumle Nov 10 '22 at 22:49
  • 1
    `int(int)` means "a function accepting int and returning int". There is no such thing as a variable that holds a function, which is why `int(int) f = ...` doesn't work. What you stumbled on is that `int(void)` is a special case: "If the only thing inside the parentheses is the literal four-letter-word `v`-`o`-`i`-`d`, then ignore it." In language-lawyer speak: "[A parameter list consisting of a single unnamed parameter of non-dependent type `void` is equivalent to an empty parameter list](https://timsong-cpp.github.io/cppwp/dcl.fct#4)." – Raymond Chen Nov 10 '22 at 23:09
  • @RaymondChen interesting that the spec you link suggests that `int(std::type_identity_t)` or `int(std::void_t)` would also be allowed to express a function with no parameters, as [this comment](https://stackoverflow.com/questions/13372173/using-void-template-arguments-in-c/13372928#comment112243324_13372928) indicates. – Patrick Roberts Nov 14 '22 at 22:38