A known bug in GCC <12 precludes initialising and immediately accessing implicitly typed vectors like so:
auto elem = (std::vector {1, 2, 3})[0];
which for some types, can be worked around using decltype
:
auto elem = (std::vector <decltype(1)> {1, 2, 3})[0];
Alas this workaround does not work for function pointers:
void f() { }
// won't compile in Clang nor GCC for unknown (by me) reason
auto elem = (std::vector <decltype(f)> {f,f,f})[0];
// won't compile in GCC due to known bug
auto elem = (std::vector {f, f, f})[0];
Why does this workaround not work for function pointers, despite it working for other types? Is there another workaround such that a vector of function pointers can be initialised and immediately accessed without explicitly declaring the pointer type (i.e. the function signature)?
Consider creating a vector of elements without explicitly declaring the element type:
auto vec = std::vector{1, 2, 3};
This compiles fine with Clang and GCC (with -std=c++17
). I wish however to avoid assigning the vector to a variable, and instead fetch an element in the same line:
auto elem = (std::vector{1, 2, 3}) [0];
This compiles fine in Clang, but fails in GCC with exception
error: missing template arguments after ‘vector<...auto...>’
| auto elem = (std::vector{1, 2, 3})[0];
| ^~~~~~
In file included from /usr/include/c++/11/vector:67,
/usr/include/c++/11/bits/stl_vector.h:389:11: note: ‘template<class _Tp, class _Alloc> class std::vector’ declared here
| class vector : protected _Vector_base<_Tp, _Alloc>
| ^~~~~~
This is an identical error to that triggered when compiling my original example in a standard earlier than C++17
. It is as if GCC fails to recognise the implicit-type syntax if it is a sub-expression. Is this a bug in GCC?
In any case, I can remedy it using decltype
:
auto elem = (std::vector <decltype(0)> {1, 2, 3}) [0];
which compiles in both Clang and GCC.
Alas, I do not actually wish to store integers, but function pointers! The below code compiles fine in Clang but fails in GCC, as we saw occur with integers.
void f() {}
auto elem = (std::vector {f, f, f}) [0];
Alas trying to use decltype
, which worked for integers, does not work for function pointers! Expression
(std::vector <decltype(f)> {f, f, f}) [0]
will fail to compile in both Clang and GNU, with errors:
In file included from /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/iostream:43:
In file included from /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/ios:222:
In file included from /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/__locale:15:
In file included from /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/__memory/shared_ptr.h:23:
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/__memory/allocator.h:151:19: error: multiple overloads of 'address' instantiate to the same signature 'const_pointer (const_reference) const noexcept' (aka 'void (*(void (&)()) const noexcept)()')
const_pointer address(const_reference __x) const _NOEXCEPT {
^
/usr/include/c++/11/ext/new_allocator.h: In instantiation of ‘class __gnu_cxx::new_allocator<void()>’:
/usr/include/c++/11/bits/allocator.h:124:11: required from ‘class std::allocator<void()>’
/usr/include/c++/11/bits/stl_vector.h:87:21: required from ‘struct std::_Vector_base<void(), std::allocator<void()> >’
/usr/include/c++/11/bits/stl_vector.h:389:11: required from ‘class std::vector<void()>’
test.cpp:25:48: required from here
/usr/include/c++/11/ext/new_allocator.h:96:7: error: ‘const _Tp* __gnu_cxx::new_allocator<_Tp>::address(__gnu_cxx::new_allocator<_Tp>::const_reference) const [with _Tp = void(); __gnu_cxx::new_allocator<_Tp>::const_pointer = void (*)(); __gnu_cxx::new_allocator<_Tp>::const_reference = void (&)()]’ cannot be overloaded with ‘_Tp* __gnu_cxx::new_allocator<_Tp>::address(__gnu_cxx::new_allocator<_Tp>::reference) const [with _Tp = void(); __gnu_cxx::new_allocator<_Tp>::pointer = void (*)(); __gnu_cxx::new_allocator<_Tp>::reference = void (&)()]’
96 | address(const_reference __x) const _GLIBCXX_NOEXCEPT
| ^~~~~~~
How can I initialise and access this inline temporary vector without explicitly specifying the type, which would of course encode the function signature and return type?
I have multiple single-integer-templated functions with distinct signatures and return-types:
template <int N> void f(int x) { ... }
template <int N> float g(int x, int y) { ... }
...
The template parameter informs compile-time optimisations in the functions (for more info, see this SO post). A simple way to choose the template parameter of the invoked function is to build an array of pointers to the function with all legal template parameters:
std::vector <void(*)(void)> f_funcs { f<0>, f<1>, f<2>, ... };
int param = 2;
f_funcs[param]();
Notice I explicitly specified the type void(*)(void)
(the signature of f
) as the vector template. I would have to hardcode the array initialisation for every one of my distinctly-typed functions:
std::vector <float(*)(int,int)> g_funcs { g<0>, g<1>, g<2>, ... };
int param = 2;
float ret = g_funcs[param](6,9);
This compiles fine, but is tedious boilerplate. I wish to avoid hardcoding an array literal of function pointers for each of my functions, especially because some of them accept two template parameters and need a 2D initialiser. So, I wrote a macro:
#define GET_FUNC(func, param) \
(vector {func<0>, func<1>, func<2>, ...} ) [param]
int param = 3;
auto f_ = GET_FUNC(f, param);
auto g_ = GET_FUNC(g, param);
f_();
float x = g_(6,9);
Use of implicit typing avoids me having to pass the signatures of f
and g
to the macro. This compiles fine in Clang, but as described above, does not compile in GCC.
Introducing decltype
...
#define GET_FUNC(func, param) \
(vector <decltype(func<0>)> {func<0>, func<1>, func<2>, ...}) [param]
does not compile in Clang nor GCC.
As commented by Jarod42, a workaround to support versions of GCC earlier than 12 is to maintain an array of explicit pointers:
(std::vector <decltype(&f)> {&f, &f, &f}) [0]
Despite the comments, this is not the (inapplicable) solution presented in this related but simpler problem. This question is not a duplicate (gee wizz)!
The solution presented by user12002570 does not work for the templated functions described in my question (godbolt).
// fails to compile in GCC < v12
auto func = ((vector {f<0>, f<1>, f<2>}))[0];