template <typename Type, Type Func>
struct A
{
};
void func();
A<void(), func> a; // same result with A<void(), &func> a;
Этот код компилируется с Clang (включая последнюю версию 8.0.0), но не с GCC (включая последнюю версию 9.1).
GCC говорит: error: 'void()' is not a valid type for a template non-type parameter
Какой компилятор прав и почему?
Я предполагаю, что GCC неверен, потому что следующее компилируется как в Clang, так и в GCC:
template <void()>
struct A
{
};
void func();
A<func> a; // same result with A<&func> a;
Таким образом, вопреки тому, что сообщает GCC в первом примере, void() кажется «допустимым типом для параметра шаблона, не являющегося типом».
Я полагаю, что здесь нет указателей (только необработанные типы функций), и GCC прав. Здесь есть какое-то связанное объяснение о типе функции и указателе на функцию: stackoverflow.com/questions/8573763/… В любом случае, для конкретного ответа нам нужен языковой юрист. Обновлено: A<void(&)(), func> a; принимается обоими компиляторами, а A<void(&&)(), func> a; снова только clang
В обновлении тип параметра нетипового шаблона — void(*)(), потому что он скорректирован. template<void()> struct A эквивалентно template<void(*)()> struct A;демо





Аналогично тому, что происходит с тип параметра функции, если тип параметра шаблона, не являющегося типом, является типом функции, он настраивается на указатель на тип функции [темп.парам]/8:
A non-type template-parameter of type “array of T” or of function type T is adjusted to be of type “pointer to T”.
Так что клан прав. Отчет об ошибке GCC уже существует ошибка №82773
Только текущий рабочий проект стандарта С++ распознает процесс замена аргумента шаблона в следующие параметры шаблона. Таким образом, можно утверждать, что стандарт не ясен, потому что он не указывает, что корректировка типа выполняется после каждой из этих замен.
Разница между Clang и GCC заключается в том, как они обрабатывают первый параметр шаблона, а не второй. Clang преобразовал первый параметр шаблона из void() в void(*)(), а GCC — нет.
@BorisRasin Первый параметр void() в обоих случаях. Clang выполняет настройку параметра шаблона, отличного от типа, но не GCC. Я все еще копаюсь в стандарте, чтобы увидеть, должна ли корректировка типа также происходить в этом случае или нет, но я подозреваю, что это неясно.
Они оба точно так же регулируют второй параметр. Это имеет точно такой же результат: A<void(), &func>. Разница в том, как они обрабатывают первый параметр.
@BorrisRasin Как я уже сказал, Clang не регулирует первый параметр. См. демо
Вы правы, Clang не настраивает первый параметр. Ваш ответ по-прежнему неверен, настройка второго параметра здесь ни при чем, так как проблема остается точно такой же с A<void(), &func>.
@BorisRasin Вы путаете термин «аргумент» и термин «параметр». Первый параметр шаблона — Type, а связанный с ним аргумент — void(). Второй параметр — Func, связанный с ним аргумент — &func. После замены первого параметра связанным с ним аргументом во второй параметр тип второго параметра будет void(). Тип второго аргумента (&func) — void(*)() . Clang измените тип второго параметра с void() на void(*)(). Таким образом, тип аргумента &func соответствует типу параметра...
@BorisRasin ... Для GCC второй тип параметра не настроен на void(*)(), а параметр шаблона, не являющийся типом, не может иметь тип функции, поэтому GCC жалуется.
Я ничего не смешиваю. Шаблон имеет два параметра. Я называю одну из них first, а другую second. Итак, еще раз, когда параметр second указан как &func, он имеет явный тип void(*)(), поэтому настройка компилятора не требуется. Тем не менее, проблема сохраняется. Следовательно: настройка компилятором второго параметра шаблона не имеет отношения к проблеме.
@BorisRasin Тип второго параметра, Func, не выводится из типа второго аргумента &func. Для вывода типа используйте auto. godbolt.org/z/-STKFd
Конечно, это не так. Никто не подсказал, что это. Как это вообще актуально?
@BorisRasion Вы написали: «Еще раз, когда второй параметр указан как &func, он имеет явный тип void (*) ()». Что приводит к двум вариантам: либо вы смешиваете аргумент и параметр, либо считаете, что тип параметра выводится из типа аргумента. Теперь я думаю, что вы должны прочитать все это завтра.
Вы правы, я перепутал термины аргумент и параметр. Извини за это.
Может быть актуально:
A<void(*)(), func> a;работает