Why doesn't universal reference apply for arrays?

Viewed 267
#include <type_traits>

template<typename T>
void f(const T&)
{
    static_assert(std::is_array_v<T>); // ok
}

template<typename T>
void g(T&&)
{
    static_assert(std::is_array_v<T>); // error
}

int main()
{
    char arr[8];
    f(arr); // ok
    g(arr); // error
}

My compiler is clang 7.0 with -std=c++17.

Why doesn't universal reference apply for arrays?

2 Answers

First of all, these are officially called "forwarding references", not "universal references".

Your static_assert fails due to the fact that T is deduced as T& when passing an lvalue to a function taking a "forwarding reference" - this is one of the special rules of "forwarding references" that apply during template argument deduction.

You can fix your assert by stripping any reference out first:

static_assert(std::is_array_v<std::remove_cvref_t<T>>); 

live example on godbolt.org


std::remove_cvref_t is a bleeding edge C++20 feature - you might want to use std::remove_reference_t instead if your compiler doesn't support it.

The relevant rule here when the template argument deduction takes place is:

Deduction from a function call
...
4. If P is an rvalue reference to a cv-unqualified template parameter (so-called forwarding reference), and the corresponding function call argument is an lvalue, the type lvalue reference to A is used in place of A for deduction.

So in your case, arr is being deduced as reference to arr and thus the static_assert fails.

Related