Consider the following simple code:
template<typename ...types>class myclass{ using payload_type = mylib::type_traits::type_or_tuple_t<types...>; template<typename... T> std::pair<bool, std::optional<payload_type>> try_push(T &&...data) { payload_type t; std::optional<decltype(t)> o; // this FAILS. //std::optional<payload_type> res; return {true, std::move(o)}; }};
Above, the type_or_tuple
helper simply converts the variadic pack to either a single type T
if it is a single type, or to a tuple of types (tuple<types...>
) if it is more than one type.
Gcc (9/10/11) cannot compile this. Particularly, it seems is is unable to properly expand payload_type
above, which is of course the type obtained through the type_or_tuple
helper:
g++-11 -O2 -std=c++17 -pedantic wtf.cpp -Wall -Wextra -D_POSIX_SOURCE=200809L -lrt -lmylib -lstdc++ -o qwtf.cpp:27:70: error: parameter packs not expanded with ‘...’: 27 | std::pair<bool, std::optional<payload_type>> try_push(T &&...data) | ^wtf.cpp:27:70: note: ‘T’
Also notice:
payload_type t; std::optional<decltype(t)> o;
compiles. But the commented line will not.
On the other hand, clang
has no trouble compiling this.
Is this a bug in gcc or is it incorrect usage of the language? Any ideas as to how to tweak this to get it to compile?
Here is a minimum reproducible example, including the definition of is_type_or_tuple
:
#include <iostream>#include <type_traits>#include<optional>#include <tuple>using namespace std;template<typename... types>struct type_or_tuple { using tup = std::tuple<types...>; using T = std::conditional_t<(std::tuple_size_v<tup> == 1), std::tuple_element_t<0, tup>, tup>; using is_tuple = std::conditional_t<(std::tuple_size_v<tup> > 1), std::true_type, std::false_type>;};template<typename... types>using type_or_tuple_t = typename type_or_tuple<types...>::T;//template<typename ...types>class myclass{ using payload_type = type_or_tuple_t<types...>; template<typename... T> std::pair<bool, std::optional<payload_type>> try_push(T &&...data) { payload_type t; std::optional<decltype(t)> o; // WILL NOT COMPILE! //std::optional<payload_type> res; return {true, std::move(o)}; }};int main(int, const char **){ myclass<unsigned> c;}