известная ошибка в GCC <12 не позволяет инициализировать и немедленно получить доступ к неявно типизированным векторам, например:
auto elem = (std::vector {1, 2, 3})[0];
что для некоторых типов можно обойти, используя decltype
:
auto elem = (std::vector <decltype(1)> {1, 2, 3})[0];
Увы, этот обходной путь не работает для указателей на функции:
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];
Почему этот обходной путь не работает для указателей на функции, хотя он работает для других типов? Есть ли другой обходной путь, позволяющий инициализировать вектор указателей функций и немедленно получить к ним доступ без явного объявления типа указателя (т. е. сигнатуры функции)?
Рассмотрите возможность создания вектора элементов без явного объявления типа элемента:
auto vec = std::vector{1, 2, 3};
Это отлично компилируется с Clang и GCC (с -std=c++17
). Однако я хочу избежать присвоения вектора переменной и вместо этого получить элемент в той же строке:
auto elem = (std::vector{1, 2, 3}) [0];
Это нормально компилируется в Clang, но не работает в GCC, за исключением
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>
| ^~~~~~
Это идентичная ошибка, возникшая при компиляции моего исходного примера в стандарте, предшествующем C++17
. Это как если бы GCC не смог распознать синтаксис неявного типа, если это подвыражение. Это ошибка в GCC?
В любом случае, я могу это исправить, используя decltype
:
auto elem = (std::vector <decltype(0)> {1, 2, 3}) [0];
который компилируется как в Clang, так и в GCC.
Увы, на самом деле я хочу хранить не целые числа, а указатели на функции! Приведенный ниже код отлично компилируется в Clang, но не работает в GCC, как мы видели с целыми числами.
void f() {}
auto elem = (std::vector {f, f, f}) [0];
Увы, попытка использовать decltype
, которая работала для целых чисел, не работает для указателей на функции! Выражение
(std::vector <decltype(f)> {f, f, f}) [0]
не сможет скомпилироваться как в Clang, так и в GNU, с ошибками:
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
| ^~~~~~~
Как я могу инициализировать и получить доступ к этому встроенному временному вектору без явного указания типа, который, конечно, будет кодировать сигнатуру функции и тип возвращаемого значения?
У меня есть несколько функций с одним целочисленным шаблоном с разными сигнатурами и типами возвращаемых значений:
template <int N> void f(int x) { ... }
template <int N> float g(int x, int y) { ... }
...
Параметр шаблона сообщает об оптимизации времени компиляции в функциях (для получения дополнительной информации см. этот пост SO). Простой способ выбрать параметр шаблона вызываемой функции — создать массив указателей на функцию со всеми допустимыми параметрами шаблона:
std::vector <void(*)(void)> f_funcs { f<0>, f<1>, f<2>, ... };
int param = 2;
f_funcs[param]();
Обратите внимание, что я явно указал тип void(*)(void)
(подпись f
) в качестве векторного шаблона. Мне пришлось бы жестко запрограммировать инициализацию массива для каждой из моих четко типизированных функций:
std::vector <float(*)(int,int)> g_funcs { g<0>, g<1>, g<2>, ... };
int param = 2;
float ret = g_funcs[param](6,9);
Это компилируется нормально, но это утомительный шаблон. Я хочу избежать жесткого кодирования литерала массива указателей функций для каждой из моих функций, особенно потому, что некоторые из них принимают два параметра шаблона и нуждаются в 2D-инициализаторе. Итак, я написал макрос:
#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);
Использование неявной типизации избавляет меня от необходимости передавать подписи f
и g
в макрос. Это нормально компилируется в Clang, но, как описано выше, не компилируется в GCC.
Представляем decltype
...
#define GET_FUNC(func, param) \
(vector <decltype(func<0>)> {func<0>, func<1>, func<2>, ...}) [param]
не компилируется ни в Clang, ни в GCC.
Это ошибка GCC (вероятно, gcc.gnu.org/bugzilla/show_bug.cgi?id=87709), исправленная в GCC 12.
@cpplearner, ах, это полезно знать, большое спасибо! Есть ли обходной путь для старых компиляторов? Мой код предназначен для библиотеки, которая нацелена на широкую (и старую) поддержку компилятора.
@AntiEarth Нет, просить mcve или предлагать использовать последнюю версию компилятора не является догматизмом. Также в посте нет mcve. Мы не можем скопировать/вставить ваш фрагмент и воспроизвести указанную проблему/ошибку. В данном посте нет #include
.
«Вы не «предложили» использовать последнюю версию компилятора…» Я буквально сказал «всегда используйте последнюю версию компилятора», а затем предоставил ссылку на обман, который сообщил об ошибке gcc, упомянутой выше cpplearner.
Обходной путь уже упомянут в дюпе. и здесь это тоже упоминалось.
В некоторых случаях функция превращается в указатель на функцию, но если вы напрямую используете правильный тип, все в порядке: (std::vector<decltype(&f)>{&f, &f, &f}) [0];
. Демо
@AntiEarth Более простой обходной путь, о котором уже упоминалось. Просто поставьте еще одну скобку, например auto elem = ((std::vector{1, 2, 3}))[0];
. Демо с gcc11.4
Как прокомментировал Jarod42, обходной путь для поддержки версий GCC до версии 12 — поддерживать массив явных указателей:
(std::vector <decltype(&f)> {&f, &f, &f}) [0]
Несмотря на комментарии, это не (неприменимое) решение, представленное в этой связанной, но более простой проблеме. Этот вопрос не является дубликатом (ну и дела)!
Решение представлено пользователем 12002570 не работает для шаблонных функций, описанных в моем вопросе (godbolt).
// fails to compile in GCC < v12
auto func = ((vector {f<0>, f<1>, f<2>}))[0];
«но не работает в GCC..» Здесь работает со всеми тремя компиляторами