Home > Back-end >  Template parameter type `void` vs explicit use of `void`
Template parameter type `void` vs explicit use of `void`

Time:05-26

In the following code why does the explicit expansion of template function foo fail to compile, yet the expansion of bar compiles successfully ? Live link - https://godbolt.org/z/o8Ea49KEb

template <typename T1, typename T2>
T1 foo(T2) { T2(42); return T1{}; };

template <typename T1, typename T2>
T1 bar(void) { T2(42); return T1{}; };

int main()
{
    foo<int, void>();    // fails

    bar<int, void>();    // works
}

Note that the template parameter T2 is used in the body of both functions and the only difference is that the function parameter to bar has been manually substituted.

This question was inspired after reading std::conditional - Invalid parameter type ‘void’ even when testing for 'void' and trying to simplify the issue.

CodePudding user response:

The rule saying a parameter list (void) is the same as the empty parameter list () is found in C Standard [dcl.fct]/4:

A parameter list consisting of a single unnamed parameter of non-dependent type void is equivalent to an empty parameter list. Except for this special case, a parameter shall not have type cv void.

The important piece for this question is "non-dependent type". The template parameter T is type-dependent, so it doesn't trigger the rule.

I presume the Standard has this rule because it would also be quite confusing (I'd say worse) if a template function which normally takes one parameter could suddenly become a zero-parameter function if an instantiation happens to make that parameter type void. With this rule, we know when a template is declared with a parameter, it really has one parameter.

CodePudding user response:

Consider the following:

template<typename T>
void foo(T) {}

void bar(void) {}

You are assuming foo<void> and bar(void) are equivalent, but they are not. foo<T> always requires an argument. So foo<void> is equivalent to a function baz(void x). This does not compile, as you cannot have a value of type void. That is the same error message you got.

In the function signature void bar(void) the void in brackets signifies something different: a function without arguments. This is a remnant of C and not recommended.

The functions bar<void>(T) and baz(void x) cannot exists, because they both require a value of type void, which cannot exist.

The solution with template functions is to provide overloads or specialisations for cases where a template instantiation would be illegal.

template<typename T>
void foo(T) {} // one argument

void foo() {}  // no arguments -> different signatures -> overload

template<typename T> 
void bar() {}  // no arguments

template<>
void bar<void> {} // still no arguments -> same signature -> specialisation
  •  Tags:  
  • c
  • Related