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?
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.