4

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?

Demo

#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 :/

glades
  • 3,778
  • 1
  • 12
  • 34
  • That looks to me like a GCC bug at a glance, failing too fatally on trying a function declaration, but worst comes to worst, you could have an alias for the type on a separate line I guess. Might be able to find this in the bug tracker. – chris Mar 08 '23 at 14:26
  • Did you enable C++17 using the `-std=c++17` compiler flag? – NathanOliver Mar 08 '23 at 14:29
  • 1
    @NathanOliver I enabled C++20 even. You can test it using the given link – glades Mar 08 '23 at 14:30

1 Answers1

3

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);
}

Demo

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • That works... I'm not even asking why – glades Mar 08 '23 at 14:39
  • @glades Glad do hear it. I'm guessing some half-implemented C++20 version of uniform initialization in gcc. Not sure either. – Ted Lyngmo Mar 08 '23 at 14:42
  • If my guess is in the right direction, the braces stop the possibility of the line being a function declaration before the compiler gets tripped up by it. – chris Mar 08 '23 at 14:44
  • @chris Could be, but it often reports that under `-Wvexing-parse` ... (even if not strictly vexing). Not sure ... – Ted Lyngmo Mar 08 '23 at 14:51