Вывод аргумента шаблона для указателя на функцию (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, и он также должен быть типом функции. Но это тип, который не может существовать! Так как это можно сделать?!

С другой стороны, в стандарте есть механизм, позволяющий вывести его, если вместо указателя вы использовали ссылку.

Так что не очень ясно, как это должно быть решено. С одной стороны, сегодняшняя формулировка сама по себе не позволяет этого делать, но, с другой стороны, механизм для этого уже существует для ссылок. Так что некоторые реализации идут вперед и делают то же самое для указателей.

Другие вопросы по тегам