Подтвердить что ты не робот

SFINAE с недопустимыми параметрами типа типа или массива?

Пожалуйста, рассмотрите этот код:

template<typename T>
char (&f(T[1]))[1];

template<typename T>
char (&f(...))[2];

int main() { char c[sizeof(f<void()>(0)) == 2]; }

Я ожидал, что он сделает SFINAE и переключит вторую перегрузку, так как подстановка T в T[1] дает

 void [1]()

Конечно, это недопустимый тип. Настройка типов параметров (array- > pointer) выполняется после подстановки параметров шаблона в параметры функции и проверки допустимых результирующих типов, таких как 14.8.2 [temp.deduct].

Но оба comeau и GCC не могут скомпилировать выше. Оба с различной диагностикой.

Комо говорит:

"ComeauTest.c", строка 2: ошибка: массив функций не разрешен char (&f(T[1]))[1];

GCC говорит (версия 4.3.3):

ошибка: ISO С++ запрещает массив нулевого размера c

Значение, GCC не подлежит замене, но он выбирает первую перегрузку f, возвращая sizeof из 1, вместо того, чтобы не подставлять ее спереди, как Comeau.

Какой компилятор прав, и мой код действительно вообще? Пожалуйста, обратитесь к соответствующему разделу "Стандарт" в вашем ответе. Спасибо!


Обновить. Сам стандарт содержит такой пример в списке в 14.8.2/2. Я не знаю, почему я сначала его не заметил:

template <class T> int f(T[5]);
int I = f<int>(0);
int j = f<void>(0); // invalid array

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

4b9b3361

Ответ 1

Небольшая заметка, хотя и очень редко, я нашел несколько случаев, когда я считают, что компилятор Комоу ошибается, хотя эти случаи настолько редки, что их всегда стоит двойной и тройной проверяя ваши предположения!

У меня может быть причина для поведения g++. Я не уверен, что точно задается при настройке параметров:

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

template<typename T>
struct A
{
  void bar (T[10]);
};

template<typename T>
void A<T>::bar (T*)
{
}

Определение "bar" является законным, поскольку "T [10]" распадается на "T *". я делаю не вижу ничего в стандарте, который запрещает компилятор из выполняя корректировки 8.3.5 против объявления шаблона, и это также улучшает производительность, когда дело доходит до соответствия перегрузки.

Применив это к вашему примеру, g++ может рассматривать его как:

template<typename T>
char (&f( T* ))[1];

template<typename T>
char (&f(...))[2];

int main() { char c[sizeof(f<void()>(0)) == 2]; }

В приведенном выше замещаемом параметре есть легальный указатель на функция, а не массив функций.

Итак, вопрос для меня - это если есть что-то, что запрещает корректировки параметров функции (8.3.5) в два раза?

Лично я считаю, что имеет смысл разрешить корректировки дважды, поскольку в противном случае это осложняет соответствие шаблона функции Перегрузки

В заключение я считаю, что для g++ допустимо выбрать первую перегрузку основанный на том, как он обрабатывает параметры разлагающегося массива, а Комо ошибается не иметь дедуктивного отказа для массива функций.

Конечно, теперь это означает, что (если бы Комо был исправлен), то каждый компилятор выбрали бы другую перегрузку и все равно были бы стандартами совместимый!: (

EDIT:

Чтобы проиллюстрировать мою мысль, рассмотрим следующий код:

template <typename T> void foo ( T * );
template <typename T> void foo ( T * const );
template <typename T> void foo ( T [] );
template <typename T> void foo ( T [10] );
template <typename T> void foo ( T [100] );

void bar () 
{
  foo < void() > ( 0 );
}

Здесь foo был объявлен и обновлен несколько раз. Какое объявление и, следовательно, какой тип параметра должен компилятор применять правила, перечисленные в 14.8.2?

Я хочу сказать, что стандарт ничего не говорит об этом. Я также хотел бы сказать, что любые формулировки по этому вопросу должны были бы оставить это как "undefined" или "поведение, определенное реализацией".

Ответ 2

Удивительно - это работает в VS2008. Я не думаю, что это обязательно свидетельствует о том, что это правильное поведение или нет, хотя...

Visual Studio интерпретирует

char (&f(T[1]))[1];

как функция, которая принимает массив размера 1 из T и возвращает ссылку на массив символов размером 1.

Ответ 3

Я попытаюсь описать процесс вычитания аргумента шаблона, поскольку я понимаю его, прочитав стандарт.

  • Явные аргументы шаблона проверяются, как описано в 14.8.2/2.
  • Результирующая сигнатура функции настраивается в соответствии с 8.3.5 (т.е. выполняется разложение матрицы на указатель).
  • Неявные аргументы шаблона выводятся согласно 14.8.2.1 (это выполняется на частично замещенной подписи с шага 2).

Вывод для первой перегрузки не выполняется на шаге 1, поэтому разрешение перегрузки возвращает вторую перегрузку. Я не верю, что программа плохо сформирована.