The following snippet compiles in gcc 12.1 but not in gcc 11.1. Unfortunately, I only have gcc 11 at hand as I'm crosscompiling for a microcontroller. Is there a way to make it work? Also what is this cryptic "use 'auto' for an abbreviated function template" all about?
#include <cstdio>
#include <tuple>
template <typename T>
struct channel
{
int a;
T b;
};
template <typename... Channels>
struct light
{
light(const std::tuple<Channels...>& channels)
: channels_ { channels }
{
}
std::tuple<Channels...> channels_;
};
int main()
{
light li(std::tuple{channel(2, 2), channel(2,false)});
}
Error (only in gcc 11.1):
<source>:25:14: error: class template placeholder 'std::tuple' not permitted in this context
25 | light li(std::tuple{channel(2, 2), channel(2,false)});
| ^~~
<source>:25:14: note: use 'auto' for an abbreviated function template
Interestingly, gcc 10.4 has something even different to say:
<source>:25:14: error: 'auto' parameter not permitted in this context
25 | light li(std::tuple{channel(2, 2), channel(2,false)});
| ^~~
I need a workaround that allows me to not specify all the template parameters of std::tuple as that would blow up my initializer to the point of unrecognizeability :/
I need a workaround ...
light li{ std::tuple{channel(2, 2), channel(2, false)} };
// ^ ^
A slightly more involved workaround could be to add deduction guides and constraints.
#include <cstdio>
#include <tuple>
#include <type_traits>
#include <string>
template <class T>
struct channel {
int a;
T b;
};
// deduction guide for channel:
template<class T>
channel(int, T) -> channel<std::remove_cvref_t<T>>;
// trait to check if a type is a channel
template<class> struct is_channel : std::false_type {};
template<class T> struct is_channel<channel<T>> : std::true_type {};
template<class T> static inline bool is_channel_v = is_channel<T>::value;
// require channel based template parameters
template <class... Channels>
requires std::conjunction_v<is_channel<Channels>...>
struct light {
template<class... Chs>
light(Chs&&... channels) : channels_{std::forward<Chs>(channels)...} {}
std::tuple<Channels...> channels_;
};
// deduction guide for light
template<class... Channels>
light(Channels...) -> light<std::remove_cvref_t<Channels>...>;
Now using (
...)
to initialize the light
works even in gcc 11.1:
int main() {
channel x{ 2, std::string("Hello") };
light li( channel(2, 2), channel(2, false), x);
}