Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

trailing return type of deduction guide is not a specialization

I'm trying to do an advanced class template argument deduction by using the new deduction guides from c++17. Unfortunately, it looks like you can only use simple template declarations after the ->, but I need a helper struct to determine the resulting type.

My use case is this one: I have a variadic template class that takes an arbitrary amount of different types. For one constructor I want to specify every single one, for another ctor I want to specify only one type and replicate it N times.

To access this N in the deduction guide I introduced a new type:

template<size_t N>
struct Replicate { };

The class I have is similar this one:

template<typename... Foos>
struct Foo {
    // [ ... ] member std::tuple<Foos...>

    // ctor 1: give values for all types (easy to deduce)
    Foo(Foos&&... args /* rvalue ref, NOT forwarding */) { };

    // ctor 2: give one value and N, result is N copies of this value. 
    // takes Replicate<N> as parameter to aid the deduction.
    template<typename T, size_t N>
    Foo(const T& t, Replicate<N>) { };
};

The usage would be like this:

Foo f1{1, 2, 3.0}; // deduce Foo<int, int, double>;
Foo f2{8, Replicate<4>{}}; // deduce Foo<int, int, int, int>; 

The deduction guide for the first one is straight forward:

template<typename... Ts>
Foo(Ts&&...) -> Foo<Ts...>;

It gets problematic with the second (ctor 2) deduction guide. First I need a helper struct to create Foo<T, T, ... /* total N times */, T> from T and N.

template<typename, typename, typename>
struct ExpandNTimes;

template<typename T, size_t... Is>
struct ExpandNTimes<T, std::index_sequence<Is...>> {
    template<size_t> using NthType = T;
    using Type = Foo<NthType<Is>...>;
};

Then in the deduction guide I want to utilize the helper to deduce the correct type. I cant directly use Foo<something> as there is no kind of "in place parameter pack creation", therefore the helper struct.

template<typename T, size_t N>
Foo(const T&, Replicate<N>) -> typename ExpandNTimes<T, std::make_index_sequence<N>>::Type;

Unfortunately this results int an error similar to this one:

error: trailing return type of 'typename ExpandNTimes<T, /* std things */>::Type' deduction guide is not a specialization of ‘Foo<Ts>’

Is there any way to work around this issue?

like image 653
nyronium Avatar asked Mar 07 '23 15:03

nyronium


1 Answers

This is impossible with class template argument deduction - both template names must be the same, and the thing after the -> must be a simple-template-id. This doesn't leave any room for template shenanigans.

But nothing prevents you from doing the thing that class template argument deduction is intended to replace: factory functions:

template <typename T, size_t N>
typename ExpandNTimes<T, std::make_index_sequence<N>>::Type
makeFoo(T const&, Repliace<N>);
like image 152
Barry Avatar answered Mar 11 '23 20:03

Barry