0

I am taking advantage of C++17 new feature to deduce the class member type in a templated class. Specifically, I am doing this:

#include <array>
#include <iostream>

template <std::size_t N = 0>
class A
{
public:
    using Array = std::array<int, N>;

    A(const Array& arr) : myArr{arr} {}
    A() = default;

    void show() { std::cout << N << std::endl; }

private:
    Array myArr;
};

When I construct A without indicating N explicitly, compiler matches correctly the constructor and deduces N correctly!

std::array<int, 3> data{1, 2, 3};
A a1{data};
A a2;

a1.show(); // prints 3
a2.show(); // prints 0

However, this magic is spoiled as soon as I introduce another template parameter in A, like this:

template <typename T, std::size_t N = 0>
class A
{
    ... // same content, no use of T
}

When I construct A passing any T like this, it does not compile anymore:

A<int> a1{data};
A<int> a2;

Compiler deduces N=0 in all cases and cannot assign the input std::array<int, 3> to the deduced parameter in constructor std::array<int, 0>:

error: no matching function for call to ‘A<int>::A(<brace-enclosed initializer list>)’
A<int> a1{data};

note: candidate: ‘A<T, N>::A(const Array&) [with T = int; long unsigned int N = 0; A<T, N>::Array = std::array<int, 0>]’
A(const Array& arr) : myArr{arr} {}

note:  no known conversion for argument 1 from ‘std::array<int, 3>’ to ‘const Array&’ {aka ‘const std::array<int, 0>&’}
A(const Array& arr) : myArr{arr} {}

Could anyone explain why this happens and if it has any solution?

Thank you in advance!

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
Victor
  • 460
  • 3
  • 11
  • 1
    When you type `A`, you specify all the template parameters, N is 0 by default. No deducing. – 273K Apr 21 '21 at 02:52
  • @S.M. That's it! I didn't realize. Then I believe there is no way to deduce `N` correctly, I need to pass `T` everywhere... – Victor Apr 21 '21 at 02:57
  • Does this answer your question? [Partial class template argument deduction in C++17](https://stackoverflow.com/questions/57563594/partial-class-template-argument-deduction-in-c17) – Davis Herring Apr 21 '21 at 03:28
  • @DavisHerring Thank you, it is a very close answer but I think I cannot make it work in my case. If I declare `template class A -> A` for cases with `N=0` then I can remove the default value of `N` here `template class A`. That is great, it will allow `A a2;`. However, I don't know how to declare cases where `N>0` without explicitly indicating `A a1{data};`. Is it possible or did I miss anything? – Victor Apr 21 '21 at 07:28
  • @Victor: I voted for the duplicate based on its explanation of the difficulty, not on the applicability of its workarounds. That’s not to say that there can’t be any: in C++20, for instance, you may be able to use something like `wrapper::alias a{data};`. – Davis Herring Apr 21 '21 at 08:46

1 Answers1

0

Deduction guides do not do partial matching.

Synthesized or not.

In the first case, the compiler synthesizes a deduction guide.

template<std::size_t N>
A(std::array<int,N>const&)->A<N>;

Once you pass any template parameters, deduction guides are not used. No partial matching.

See https://en.cppreference.com/w/cpp/language/class_template_argument_deduction for a decent description of what you called magic.

Write a make function if you want, or omit int.

As to why, probably reverse compatibility fears and the KISS principle.

Captain Giraffe
  • 14,407
  • 6
  • 39
  • 67
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524