Ошибка замены не является ошибкой - Substitution failure is not an error

Ошибка замены не является ошибкой (СФИНАЕ) относится к ситуации в C ++ где неверная замена шаблон параметры сами по себе не являются ошибкой. Дэвид Вандевурд первым ввел аббревиатуру SFINAE для описания связанных методов программирования.[1]

В частности, при создании набора кандидатов для разрешение перегрузки, некоторые (или все) кандидаты этого набора могут быть результатом создания экземпляров шаблонов с (потенциально выведенными) аргументами шаблона, замененными соответствующими параметрами шаблона. Если ошибка возникает во время замены набора аргументов для любого заданного шаблона, компилятор удаляет потенциальную перегрузку из набора кандидатов вместо остановки с ошибкой компиляции, при условии, что ошибка замены является одной из тех, которые стандарт C ++ предоставляет такую ​​обработку.[2] Если один или несколько кандидатов остаются и разрешение перегрузки завершается успешно, вызов правильно сформирован.

Пример

В следующем примере показан базовый экземпляр SFINAE:

структура Тест {  typedef int фу;};шаблон <typename Т>пустота ж(typename Т::фу) {}  // Определение # 1шаблон <typename Т>пустота ж(Т) {}  // Определение # 2int главный() {  ж<Тест>(10);  // Вызов №1.  ж<int>(10);   // Вызов №2. Без ошибки (хотя int :: foo нет)                // спасибо СФИНАЕ.}

Здесь попытка использовать неклассовый тип в квалифицированном имени (T :: foo) приводит к неудачному вычету для f потому что int не имеет вложенного типа с именем фу, но программа сформирована правильно, потому что допустимая функция остается в наборе функций-кандидатов.

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

Например, SFINAE можно использовать, чтобы определить, содержит ли тип определенный typedef:

#включают <iostream>шаблон <typename Т>структура has_typedef_foobar {  // Типы "да" и "нет" гарантированно имеют разные размеры,  // конкретно sizeof (yes) == 1 и sizeof (no) == 2.  typedef char да[1];  typedef char нет[2];  шаблон <typename C>  статический да& тест(typename C::foobar*);  шаблон <typename>  статический нет& тест(...);  // Если "sizeof" результата вызова test  (nullptr) равен  // sizeof (да), первая перегрузка сработала и у T есть вложенный тип с именем  // foobar.  статический const bool ценить = размер(тест<Т>(nullptr)) == размер(да);};структура фу {  typedef плавать foobar;};int главный() {  стандартное::cout << стандартное::булальфа;  стандартное::cout << has_typedef_foobar<int>::ценить << стандартное::конец;  // Выводит false  стандартное::cout << has_typedef_foobar<фу>::ценить << стандартное::конец;  // Выводит истину}

Когда Т имеет вложенный тип foobar определено, создание первого тест работает, и константа нулевого указателя успешно передана. (И результирующий тип выражения да.) Если не работает, единственная доступная функция - вторая тест, и результирующий тип выражения нет. Многоточие используется не только потому, что он принимает любой аргумент, но и потому, что его ранг преобразования самый низкий, поэтому вызов первой функции будет предпочтительнее, если это возможно; это устраняет двусмысленность.

Упрощение C ++ 11

В C ++ 11, приведенный выше код можно упростить до:

#включают <iostream>#включают <type_traits>шаблон <typename... Ц>с помощью void_t = пустота;шаблон <typename Т, typename = пустота>структура has_typedef_foobar : стандартное::false_type {};шаблон <typename Т>структура has_typedef_foobar<Т, void_t<typename Т::foobar>> : стандартное::true_type {};структура фу {  с помощью foobar = плавать;};int главный() {  стандартное::cout << стандартное::булальфа;  стандартное::cout << has_typedef_foobar<int>::ценить << стандартное::конец;  стандартное::cout << has_typedef_foobar<фу>::ценить << стандартное::конец;}

Благодаря стандартизации идиомы обнаружения в Библиотека фундаментальная v2 (n4562) предложение, приведенный выше код можно переписать следующим образом:

#включают <iostream>#включают <type_traits>шаблон <typename Т>с помощью has_typedef_foobar_t = typename Т::foobar;структура фу {  с помощью foobar = плавать;};int главный() {  стандартное::cout << стандартное::булальфа;  стандартное::cout << стандартное::is_detected<has_typedef_foobar_t, int>::ценить << стандартное::конец;  стандартное::cout << стандартное::is_detected<has_typedef_foobar_t, фу>::ценить << стандартное::конец;}

Разработчики Способствовать росту использовал SFINAE в boost :: enable_if[3] и другими способами.

Рекомендации

  1. ^ Вандевурде, Дэвид; Николай М. Йосуттис (2002). Шаблоны C ++: полное руководство. Эддисон-Уэсли Профессионал. ISBN  0-201-73484-2.
  2. ^ Международная организация по стандартизации. «ISO / IEC 14882: 2003, Языки программирования - C ++», § 14.8.2.
  3. ^ Boost Enable If