Home > Back-end >  std::enable_if_t and std::array implementation
std::enable_if_t and std::array implementation

Time:08-19

How can I use this function? (syntax)

template<typename T, typename = std::enable_if_t<std::is_array_v<T>>>
void foo(T&& a)
{
  std::cout << a.size() << std::endl;
}

because this is error

std::array<std::string, 3> arr { "1", "2", "3" };
foo<std::array<std::string, 3>>(arr);

this error too

std::array<std::string, 3> arr { "1", "2", "3" };
foo<>(arr);

this error too

std::array<std::string, 3> arr { "1", "2", "3" };
foo<std::array<std::string, 3>, 3>(arr);

link: https://godbolt.org/z/ToMeGe5d5

CodePudding user response:

The function is constrained so that it accepts only built-in arrays as argument, not std::array. std::is_array_v tests only for built-in arrays.

Also, functions with forwarding references as function parameter (T&& where T is a template parameter) are not expected to have the corresponding type template argument (for T) specified explicitly. So just drop the template argument list in the call completely. It is supposed to be deduced, otherwise the forwarding reference will not work as intended.

For example:

std::string arr[] = { "1", "2", "3" };
foo(arr);

But then the function specialization will fail to instantiate, because built-in arrays don't have members that can be referred to with the member access operator as in a.size(). So the function is not usable at all. (I am not sure whether you made a mistake here reducing the actual function to an example or whether you are trying to write a function accepting std::array yourself, in which case the other answers are giving good suggestions.)

CodePudding user response:

std::is_array_v<std::array<T, N>> yields false for any T and N. You need to create a custom type trait or simply write the function differently:

template<typename T, size_t N>
void foo(std::array<T, N> const& a)
{
    std::cout << a.size() << std::endl;
}

or

template<class T>
struct is_std_array : std::false_type {};
 
template<class T, size_t N>
struct is_std_array<std::array<T, N>> : std::true_type {};

template<class T>
constexpr bool is_std_array_v = is_std_array<std::remove_cvref_t<T>>::value;

template<typename T, std::enable_if_t<is_std_array_v<T>, bool> = false>
void foo(T&& a)
{
    std::cout << a.size() << std::endl;
}

See godbolt

CodePudding user response:

That function is (almost[1]) uncallable as written:

  • std::is_array_v is only true for built-in C-style arrays. That is std::is_array_v<int[10]> is true, but std::is_array_v<std::array<int, 10>> is false.
  • Built-in C-style arrays do not have a size member function.

That means that for any parameter type that will satisfy your type constraint, the body of the function will fail to compile.


You don't really need a type constraint though. You can just make your parameter a std::array:

template<typename T, size_t N>
void foo(const std::array<T, N>& a)
{
  std::cout << a.size() << std::endl;
}

int main()
{
    std::array<int, 4> arr = {1, 2, 3, 4};
    foo(arr); // prints 4
}

Or if you're trying to get the size of a built-in C-style array:

template<typename T, size_t N>
void foo(const T(&a)[N])
{
  std::cout << N << std::endl;
}

int main()
{
  int arr[] = {1, 2, 3, 4};
  foo(arr); // prints 4
}

[1]: Technically foo<std::array<std::string, 3>&, void>(arr) works, but that's just because you've implemented the type check in such a way that it can be worked around by supplying something in place of the default template argument.

  •  Tags:  
  • c
  • Related