I am working on a class that accepts a tuple of types of messages this obect will receive. In our model, we're using void
to express the existence of a message that does not carry data, just a signal. The tuple is never actually instantiated but we inspect its types and generate various callbacks based on them. I have seen several references that suggest the use of std::tuple as replacement for variadic templates and the use of void in std::tuple
(e.g., Void type in std::tuple).
All seems to work well, until I introduce a virtual member function that calls an external function:
#include <tuple>
void foo(void *);
template <typename TupleT>
class TT {
public:
TT(){}
void bar() {
}
void work() {
bar(); // OK
foo(this); // UPDATE: NOT OK
}
virtual void fail() {
bar(); // OK
foo(this); // NOT OK
}
};
TT<std::tuple<void>> tt;
The compiler error I get tells me that the std::tuple<void>
is actually instantiated in the virtual member function:
In file included from <source>:1:
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/tuple: In instantiation of 'struct std::_Head_base<0, void, false>':
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/tuple:407:12: required from 'struct std::_Tuple_impl<0, void>'
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/tuple:599:11: required from 'class std::tuple<void>'
<source>:26:10: required from 'void TT<TupleT>::fail() [with TupleT = std::tuple<void>]'
<source>:25:18: required from here
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/tuple:182:17: error: forming reference to void
182 | constexpr _Head_base(const _Head& __h)
| ^~~~~~~~~~
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/tuple:219:7: error: forming reference to void
219 | _M_head(_Head_base& __b) noexcept { return __b._M_head_impl; }
| ^~~~~~~
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/tuple:222:7: error: forming reference to void
222 | _M_head(const _Head_base& __b) noexcept { return __b._M_head_impl; }
| ^~~~~~~
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/tuple:224:13: error: 'std::_Head_base<_Idx, _Head, false>::_M_head_impl' has incomplete type
224 | _Head _M_head_impl;
| ^~~~~~~~~~~~
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/tuple:224:13: error: invalid use of 'void'
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/tuple: In instantiation of 'struct std::_Tuple_impl<0, void>':
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/tuple:599:11: required from 'class std::tuple<void>'
<source>:26:10: required from 'void TT<TupleT>::fail() [with TupleT = std::tuple<void>]'
<source>:25:18: required from here
Can someone explain to me why the compile attempts to instantiate the tuple in a virtual member function?
The obvious workaround seems to be to call a non-virtual member function and move the external call into it (calling works()
from fail()
in the example instead of calling foo()
directly). While this works, it is rather unsatisfactory and I'd like to understand what happens here.
UPDATE: As pointed out in the comments, a call to work()
causes the same problem (I thought I had tested it but compiled the wrong file...). I'd still like to understand why passing a pointer to TT
has the compiler instantiate the tuple...?
Thanks for any hints :)