SFINAE с недопустимыми параметрами типа функции или типа массива? - PullRequest
22 голосов
/ 05 мая 2009

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

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

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

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

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

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

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

ошибка: ISO C ++ запрещает массив нулевого размера 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

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

Ответы [ 3 ]

12 голосов
/ 05 мая 2009

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

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

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

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?

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

1 голос
/ 05 мая 2009

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

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

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

1 голос
/ 05 мая 2009

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

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

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

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

...