c++arraysc++11perfect-forwarding

Can I forward an array type to a function invoked via pointer perfectly, without the argument decaying?


I have an issue that boils down to an example like this:

#include <cstdlib>
#include <utility>

// class that is constructible from a `float` array type
struct data
{
    template <size_t N>
    data(float (&arr)[N]) { }
};

// function that takes an instance of `data`
void direct(data) { }
// pointer to the above function
void (*func_ptr)(data) = &direct;

// forward the argument to the function directly
template <typename T>
void call_direct(T && t)
{
    return direct(std::forward<T>(t));
}

// forward the argument to the function invoked via pointer
template <typename T>
void call_via_ptr(T && t)
{
    return (*func_ptr)(std::forward<T>(t));
}

int main()
{
    float arr[4];

    call_direct(arr);
    call_via_ptr(arr);
}

That is, I have a function direct() that takes an argument of type data. data is constructible from a fixed-length C style array. I have another pair of functions, call_direct() and call_via_ptr(), which forward their single template argument to the function, either invoking it directly or via a function pointer.

The call_direct() approach works fine. However, the call_via_ptr() fails to compile on gcc versions through 14.2, yielding an error like:

<source>: In instantiation of 'void call_via_ptr(T&&) [with T = float (&)[4]]':
<source>:35:17:   required from here
   35 |     call_via_ptr(arr);
      |     ~~~~~~~~~~~~^~~~~
<source>:27:39: error: could not convert '(float*)std::forward<float (&)[4]>((* & t))' from 'float*' to 'data'
   27 |     return (*func_ptr)(std::forward<T>(t));
      |                        ~~~~~~~~~~~~~~~^~~
      |                                       |
      |                                       float*

However, it does compile fine on all clang versions that I have tried.

It looks like when calling the function via a function pointer, the array argument is decaying to a float *, which is not convertible to data, hence the error. However, it isn't clear to me why the pointer decay would work differently between the two cases.

Is there a tweak I can make here to allow this to build on gcc? Which compiler is correct here?


Solution

  • This is a documented bug in GCC. That is to say that clang (and msvc) are correct.

    Here is the "meta-bug" covering a few variations on it. Like this which applies even when there is no template.

    clang is correct because zero standard conversions should be required to match the target function.