Search code examples
c++templatesc++20template-argument-deduction

C++ Class types in non-type template parameters: deduction guide fails


Problem

I am using a c++2a feature which allows structs / std::array as template arguments (g++-9.2.0, not supported in clang yet). The feature is called Class types in non-type template parameters and proposed in P0732R2.

I try to use templates parameters of one class (struct C in the examples below) in order to deduce corresponding class template parameters of a second class (struct B in the examples below). I hereby make use of a custom class template parameter deduction guide I wrote for that specific purpose.

In this minimal example, the information I want to extract are two ints. If I use these primitive types as template parameters, everything works fine. However, when I combine the information in one std::pair or a custom std::struct, the deduction fails.

Code

Separated Information

The code below works just fine.

#include <array>

/// Data structure which contains a constexpr context to be used for type deduction later
template <int aa, int ab> struct C {};

/// Class which has to find out its own type
template <std::size_t count, std::array<int, count> a, std::array<int, count> b> struct B {
  template <int... aa, int... bb> explicit B(C<aa, bb> ... c) {}
};

/// Class deduction guide
template <int... aa, int... ab> B(C<aa, ab>... c)
    ->B<sizeof...(aa) + 1, std::array<int, sizeof...(aa) + 1>{aa...},
        std::array<int, sizeof...(aa) + 1>{ab...}>;

int main() { B k{C<1, 2>{}, C<2, 3>{}}; }

Combined information

The code below fails to compile.

#include <array>

/// Change: A contains the information from the previous example in a structs.
struct A { int a; int b; };

/// Data structure which contains a constexpr context to be used for type deduction later
template <A a> struct C {};

/// Class which has to find out its own type
template <std::size_t count, std::array<A, count> a> struct B {
  template <A... af> explicit B(C<af> ... c) {}
};

/// Class deduction guide
template <A... af> B(C<af>... c)->B<sizeof...(af) + 1, std::array<A, sizeof...(af) + 1>{af...}>;

int main() { B k{C<A{1, 2}>{}, C<A{2, 3}>{}}; }

The error output:


main.cc: In function ‘int main()’:
main.cc:24:14: error: class template argument deduction failed:
   24 |   B k {c1, c2};
      |              ^
main.cc:24:14: error: no matching function for call to ‘B(C<A{1, 2}>&, C<A{1, 2}>&)’
main.cc:17:20: note: candidate: ‘B(C<((const A)af)>...)-> B<(sizeof... (af) + 1), std::array<A, (sizeof... (af) + 1)>{(const A)af ...}> [with A ...af = {}]’
   17 | template <A... af> B(C<af>... c)->B<sizeof...(af) + 1, std::array<A, sizeof...(af) + 1>{af...}>;
      |                    ^
main.cc:17:20: note:   candidate expects 0 arguments, 2 provided
main.cc:14:31: note: candidate: ‘template<long unsigned int count, std::array<A, count> a, A ...af> B(C<((const A)af)>...)-> B<count, a>’
   14 |   template <A... af> explicit B(C<af> ... c) {}
      |                               ^
main.cc:14:31: note:   template argument deduction/substitution failed:
main.cc:24:14: note:   couldn’t deduce template parameter ‘count’
   24 |   B k {c1, c2};

I now wonder what causes this problem. Does the error occur because

  • ... what I want to achieve is not possible in general
  • ... something is not implemented yet in g++
  • ... I screwed up with my deduction guide?

I also do not understand the error message. It seems like zero arguments are expected for the function. Is the problem that C<af>... cannot be expanded in the constructor?


Solution

  • @AndiG and @walnut anwered my question with their comments to my original question.

    My issue is probably caused by a bug in my version of G++-9. I currently do not use the newest version of g++-9, the bug is solved at least in g++-10. In the version of g++-10.0 I compiled (3684bbb022c) I do not receive the error anymore.