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’
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.
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;
};
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) (i.e. void actually spelled out as a type).
I am afraid the only way you can solve this is indeed by specialising for void.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With