简体   繁体   中英

Invalid void parameter when combining template parameters to form a function signature

Trying to take the signatures of two callbacks and generate a callback signature that uses each of their return values.

Given Callbacks A and B => Generate F

Ex 1) A: int(char) B: double(bool) => F: double(int)

Ex 2) A: void(char) B: void(int) => F: void(void)

Ran into a strange compiler error when instantiating a callback with void as a parameter:

error: invalid parameter type 'void'

Problematic Code

template<class Signature>
struct my_func;

template<class Ret, class... Args>
struct my_func<Ret(Args...)>
{};

template<class FuncA, class FuncB>
struct my_fwd;

template<class ORet, class... OArgs,
         class Ret, class... Args>
struct my_fwd<
  my_func<ORet(OArgs...)>,
  my_func<Ret(Args...)>
  >
{
  my_func< ORet(Ret) > func;  // <--- error
};

int main(int, char *[])
{
  my_func<void(int)> my3;  // (1)
  my_func<void(void)> my4; // (2)
  my_func<void()> my5;     // (3)

  my_fwd< decltype(my3), my_func<void(char)> > fwd1; // (4)
  my_fwd< decltype(my3), decltype(my4) > fwd2;       // (5)

  return 0;
}

Though there are no problems with the instantiation of my_func 's with void (1), (2), (3) , the my_fwd 's (4) (5) fail, and I would like to understand why.

Workaround?!

I've found a workaround, by specializing my_fwd for Ret == void :

// with this specialization i can avoid the error
template<class ORet, class... OArgs,
         class... Args>
struct my_fwd<
  my_func<ORet(OArgs...)>,
  my_func<void(Args...)>
  >
{
  my_func< ORet() > func;
};

Question

What's the difference between the compiler trying to instantiate inside

my_fwd< my_func<void(int)>, my_func<void(char)> > :

-> my_func<void(void)> func

and

the manual version in main(): my_func<void(void)> my4 ?

Was the void specialization the correct approach for a fix? Alternatives? I'm obviously not excited about specializations and code duplication.

The difference between instatiating with void as a template parameter and manually writing void (void) is that the latter does not produce a function taking a void parameter. Listing (void) as a function's parameters is a syntactic construct whose meaning is the same as () . It's a legacy from C, where () means "parameters are unspecified" and (void) means "no parameters." C++ removed the unspecified case, and () means "no parameters" there.

However, template instantiation happens long after syntactic processing, so actually trying to instantiate a function's parameters (T) with T = void results in an error. Just as it would be an error to try to declare a function taking (std::remove_reference<decltype(std::declval<void*>())>::type) (ie void actually spelled out as a type).

I am afraid the only way you can solve this is indeed by specialising for void .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM