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

StackOverflow https://stackoverflow.com/questions/822059

  •  03-07-2019
  •  | 
  •  

Вопрос

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

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

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

Это было полезно?

Решение

Небольшое замечание, хотя и очень редкое, я нашел несколько случаев, когда я полагаю, что компилятор 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 был исправлен), то каждый компилятор выбрал бы другую перегрузку и по-прежнему соответствовал бы стандартам !:(

Редактировать:

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

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?

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

Другие советы

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

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

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

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

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

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

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top