19

In the following code:

template<size_t N>
int b(int q, const std::array<int, N>& types)
{
    int r = q;
    for (int t : types)
    {
        r = r + t;
    }
    return r;
}

int main()
{
    b<2>(9, { 2,3 });
}

How can I avoid having to specify 2 in the call to b for N? Why can't this type be automatically deduced? With out it I get the error:

'b': no matching overloaded function found 'int b(int,const std::array &)': could not deduce template argument for 'N'

ildjarn
  • 62,044
  • 9
  • 127
  • 211
paulm
  • 5,629
  • 7
  • 47
  • 70

4 Answers4

22

C++17 std::array class template argument deduction (CTAD)

Starting with C++17, this new language feature is now used by the standard library and now allows us to omit the template types as well so that the following works:

main.cpp

#include <array>

int main() {
    std::array a{1, 2, 3};
}

instead of std::array<int, 3> a{1, 2, 3};

Tested with:

g++ -ggdb3 -O0 -std=c++17 -Wall -Wextra -pedantic -o main.out main.cpp

If we set -std=c++14 instead for example, it fails to compile with:

error: missing template arguments before ‘a’

Tested on Ubuntu 18.04, GCC 7.5.0.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
8

Template argument deduction relies on direct type matching between actual argument and formal argument. The actual argument is an initializer list. It doesn't match the array type (at best it could match the internal raw array in a std::array, but the language rules don't support that).

Instead you can just use a raw array, to wit:

#include <stddef.h>
#include <array>

template<size_t N>
int b(int q, int const (&types)[N] )
{
    int r = q;
    for (int t : types)
    {
        r = r + t;
    }
    return r;
}

int main()
{
    b( 9, { 2, 3 } );
}

Or, if you don't absolutely need N at compile time, you can use a std::initializer_list.

There are also many other possibly relevant approaches (e.g. variadic template function, or defining an operator to build up a std::vector), but it's difficult to say what would suit your undisclosed purpose.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • Ah sorry, my purpose is to pass an array of ints which will be checked against the int mType of a struct, raw array seems good enough for what I'm doing anyway – paulm Oct 08 '16 at 15:02
5

Since C++20 we can use std::to_array() for type and size deduction.

In your case:

int b(int q, const auto& types)
{
    int r = q;
    for (int t : types)
    {
        r = r + t;
    }
    return r;
}

b(9, std::to_array({2, 3}));
// --- or ---
b(9, std::to_array<int>({2, 3}));

Personally I prefer std::span (or gsl::span) in function parameters.

CoolCmd
  • 939
  • 8
  • 13
1

For deducing std::array size you can use generic lambdas (C++14):

auto b = [](int q, const auto& types) {
  int r = q;
  for (int t : types)
  {
      r = r + t;
  }
  return r;
};

int main() {
    std::array<int, 2> arr = {{2,3}};
    b(9, arr);
}

When std::make_array(it's in Library Fundamentals TS) will get implemented by compilers (or you will use some own implementation of it) you won't also need to pass array size when constructing, just use

b(9, std::make_array(2,3));
Community
  • 1
  • 1
Marqin
  • 1,096
  • 8
  • 17
  • 8
    Readers should note that the `auto` here doesn't do anything that an ordinary `template` wouldn't do. This code works because the type of the actual argument is specified as `std::array`, not because of the `auto`. With this answer's `main` the OP's definition of `b` would also work. – Cheers and hth. - Alf Oct 08 '16 at 15:07