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

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

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 line и попробуйте скомпилировать код с помощью 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++ прав, то почему существует ли такая асимметрия с нормальными функциями против шаблонных функций относительно необходимости явного адреса оператора?

1 ответов


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

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

(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).

перегруженного имени функции может предшествовать оператор&. Перегруженное имя функции не должно использовать без аргументов в контексты, отличные от перечисленных. [ Примечание: любой избыточный набор скобок окружающие перегруженного имени функции игнорируется (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.