Почему нельзя сравнивать указатель на функцию шаблона без имени явного имени & on function?

Рассмотрим следующий код:

void func(int) {}
template<typename T> void templatedFunc(T) {}
int main()
{
    void (*p)(int) = func;

    bool test1 = p==func;
    //bool test2 = p==templatedFunc<int>; // compilation error
    bool test3 = p==&templatedFunc<int>; // but this works
}

Если вы раскомментируете строку test2 и попытаетесь скомпилировать код с g++, вы получите следующую ошибку:

test.cpp: In function ‘int main()’:
test.cpp:8:21: error: assuming cast to type ‘void (*)(int)’ from overloaded function [-fpermissive]
     bool test2 = p==templatedFunc<int>; // compilation error
                     ^~~~~~~~~~~~~~~~~~

Получаю этот результат в g ​​++ 5.3.0 и 6.2.0. В то же время компиляция с помощью clang++ 3.6.0 завершается без предупреждений.

Какой компилятор корректен в соответствии со стандартом здесь - g++, который дает ошибку или clang++, что нет?

И если g++ прав, то почему существует такая асимметрия с нормальными функциями vs templated functions относительно необходимости явного адреса оператора?

Ответ 1

Это ошибка gcc, и вы находитесь в углу, в стандарте С++, адрес перегруженной функции. §13.4 ([over.over]/1):

Использование перегруженного имени функции без аргументов разрешается в определенных контекстах для функции, указатель на функцию или указатель на функцию-член для определенной функции из набора перегрузки. Функция имя шаблона считается названным набором перегруженных функций в таких контекстах. Выбранная функция это тот, тип которого идентичен типу функции целевого типа, требуемого в контексте. [ Заметка: То есть класс, членом которого является член, игнорируется при сопоставлении функции указатель-член тип. - конечная нота] Цель может быть:

(1.1) - инициализируется объект или ссылка (8.5, 8.5.3, 8.5.4),

(1.2) - левая часть задания (5.18),

(1.3) - параметр функции (5.2.2),

(1.4) - параметр определяемого пользователем оператора (13.5),

(1.5) - возвращаемое значение функции, операторной функции или преобразования (6.6.3),

(1.6) - явное преобразование типа (5.2.3, 5.2.9, 5.4) или

(1.7) - несимметричный шаблон-параметр (14.3.2).

Перегруженному имени функции может предшествовать оператор and. Перегруженное имя функции не должно использовать без аргументов в других контекстах, кроме перечисленных. [Примечание: любой избыточный набор круглых скобок окружающее перегруженное имя функции игнорируется (5.1). - конечная нота]

Вы видите, чего не хватает в списке из (1.1) - (1.7)... встроенных операторов!

Если вы объявляете перегрузку operator ==, gcc не будет жаловаться на сравнение, более того, вам не нужно явно специализировать функцию шаблона:

void func(int) {}
template<class T>
void templatedFunc(T) {}
struct s{};
bool operator==(s, void(*)(int)){return false;}
int main()
{
   void (*p)(int) = templatedFunc;

   bool test1 = p==func;
   bool test2 = s{} == templatedFunc<int>; // no error - no overload resolution
   bool test3 = s{} == templatedFunc; // no error - overload resolution
   bool test4 = p == templatedFunc<int>; // gcc error, but not an error -
                                         // no overload resolution
 //bool test5 = p == templatedFunc; // error - overload resolution not
                                 // performed for built-int operators

}

test2 и test3 компилируется с помощью gcc. test4 не компилируется на gcc, но нет разрешения перегрузки, вы явно специализировали функцию. Это действительно должно скомпилировать. test5 не компилируется, как указано в стандарте. В этом случае gcc выдает то же сообщение об ошибке, что и для test4. Это, безусловно, ошибка gcc.