Вывод аргумента шаблона для указателя на функцию (g++ и ICC против Clang++ и VC++)
Рассмотрим следующую программу:
#include <iostream>
template <typename T>
void foo(const T* x) {
x();
}
void bar() { std::cout<<"bar() is called\n"; }
int main() {
foo(bar);
}
Хорошо компилируется на clang++
& VC++
но g++
выдает следующую ошибку компилятора (см. живую демонстрацию здесь)
main.cpp: In function 'int main()':
main.cpp:10:9: error: no matching function for call to 'foo(void (&)())'
foo(bar);
^
main.cpp:3:6: note: candidate: template<class T> void foo(const T*)
void foo(const T* x) {
^~~
main.cpp:3:6: note: template argument deduction/substitution failed:
main.cpp:10:9: note: types 'const T' and 'void()' have incompatible cv-qualifiers
foo(bar);
^
Я использовал -pedantic-errors
когда используешь g++
& clang++
и я использовал /W4
& /Za
опция при использовании компилятора VC++. Смотрите демо здесь и здесь. Итак, я хочу знать, как параметр типа шаблона T будет выведен здесь? Если я удалю const
из программы, то он прекрасно компилируется на g++
также. Если я использую const T&
затем он прекрасно компилируется на всех 3 компиляторах. Итак, как именно тип будет выведен здесь в этих случаях?
Обновить:
Эта программа не может быть скомпилирована на компиляторе Intel C++. Смотрите живое демо здесь. Итак, эта ошибка в g++
& Intel C++ или ошибка в Clang++
& VC++?
1 ответ
По сути, это проблема CWG 1584:
Не ясно, правильно ли сформировано следующее:
void foo(){} template<class T> void deduce(const T*) { } int main() { deduce(foo); }
Реализации варьируются в трактовке этого примера.
Который в настоящее время все еще активен. На самом деле невозможно сказать, какой компилятор прав. Хотя, как отмечается в примечании 2015 года, в CWG в настоящее время существует консенсус в отношении того, что это следует отклонить.
Чтобы дать немного больше контекста, мы должны помнить, что тип функции с cv-qualifier-seq имеет особое значение (думаю, функции-члены), и это не просто тип, который обозначает что-то, что не может быть изменено. Более того, вы даже не можете добавить квалификацию cv каким-то хитрым способом, как показано в [dcl.fct]/7:
Эффект cv-qualifier-seq в деклараторе функции - это не то же самое, что добавление cv-qualification поверх типа функции. В последнем случае cv-квалификаторы игнорируются. [Примечание: тип функции, имеющий cv-qualifier-seq, не является cv-квалифицированным типом; нет cv-квалифицированных типов функций. - примечание конца][Пример:
typedef void F(); struct S { const F f; // OK: equivalent to: void f(); };
- конец примера]
В языке нет способа сформировать константный квалифицированный тип функции. И все же, дедукция нам нужна, чтобы иметь const T
выводится как void()
, Первый является квалифицированным типом const, и он также должен быть типом функции. Но это тип, который не может существовать! Так как это можно сделать?!
С другой стороны, в стандарте есть механизм, позволяющий вывести его, если вместо указателя вы использовали ссылку.
Так что не очень ясно, как это должно быть решено. С одной стороны, сегодняшняя формулировка сама по себе не позволяет этого делать, но, с другой стороны, механизм для этого уже существует для ссылок. Так что некоторые реализации идут вперед и делают то же самое для указателей.