G++ и clang++ различное поведение с указателем на функции вариационного шаблона
Другой, кто прямо между g++ и clang++? вопрос для стандартных гуру С++.
Код следующий
template <typename ...>
struct bar
{ };
template <typename ... Ts>
void foo (bar<Ts...> const &)
{ }
int main ()
{
foo<int>(bar<int, long>{}); // g++ and clang++ compile
(*(&foo<int>))(bar<int, long>{}); // g++ and clang++ give error
(&foo<int>)(bar<int, long>{}); // clang++ compiles; g++ gives error
}
Функция шаблона foo()
принимает параметр вариационного шаблона bar
.
Первый вызов
foo<int>(bar<int, long>{}); // g++ and clang++ compile
работает как для clang++ ang g++.
Если я правильно понял, foo<int>
высвечивается только первый параметр шаблона, и это не завершает список параметров Ts...
. Таким образом, компилятор просматривает аргумент (объект bar<int, long>
) и выводит полный список.
Второй вызов отличается
(*(&foo<int>))(bar<int, long>{}); // g++ and clang++ give error
Если я правильно понимаю, с (&foo<int>)
мы получаем указатель на экземпляр foo
, где Ts...
- это точно int
(не только первый тип списка, но и весь список) и разыменовывая его (< *(&foo<int>)
) и вызывая его с неправильным аргументом (объект bar<int, long>
), мы получаем (clang++ и g++) ошибку компиляции.
До сих пор так хорошо.
Проблема возникает при третьем вызове
(&foo<int>)(bar<int, long>{}); // clang++ compiles; g++ gives error
что я был убежден (может быть, я был неправ) эквивалентен второй (мы фиксируем все типы шаблонов в Ts...
, тогда мы вызываем функцию с неправильным параметром), но g++ кажется соглашающимся (и дает ошибку), где clang++ не согласен (и скомпилировать без проблем).
Вопрос, как обычно, таков: кто прав?
Ответы
Ответ 1
Рассмотрим более простой случай:
template <class A, class B>void foo(B) {};
int main()
{
(&foo<char>)(1);
}
clang++ компилирует это, а g++ терпит неудачу со следующим сообщением:
error: адрес перегруженной функции без информации контекстного типа
Такое же сообщение дается, например, эта программа:
void moo(int) {};
void moo(int*){};
int main()
{
&moo != nullptr;
}
По-видимому, цель состоит в том, чтобы ссылаться на [over.over], в котором говорится об использовании адреса перегруженной функции. Это место в стандарте указывает, в каких контекстах может использоваться перегруженное имя функции (RHS присвоения, аргумент в вызове функции и т.д.). Однако он говорит
Перегруженное имя функции не должно использоваться без аргументов в других контекстах, кроме перечисленных. (акцент мой)
Теперь, foo
в (&foo<char>)(1)
используется без аргументов? g++, похоже, тонет. Однако он с радостью компилирует
(&moo)(1);
из второго примера. Поэтому у нас есть хотя бы некоторая несогласованность. Те же правила регулируют выбор адреса шаблона функции и перегруженного набора, поэтому (&foo<char>)(1)
и (&moo)(1)
должны быть либо действительными, либо оба недопустимыми. Сам стандарт, по-видимому, указывает, что они должны быть действительными:
Перегруженному имени функции может предшествовать оператор и [[]] [Примечание: любой избыточный набор круглых скобок, окружающих перегруженное имя функции, игнорируется (5.1). - конечная нота]