Я собирался задать тот же самый вопрос, но он уже был задан.К сожалению, ни один из существующих ответов действительно не ответил на вопрос.По крайней мере, не для меня.Я должен был разгадать это.Я хотел задать тот же вопрос, что и ОП, плюс несколько.Мой вопрос: WTF - это is_class_tester (void (U::*)(void))
материал, и как эта конструкция работает в контексте SFINAE (Ошибка замены не является ошибкой)?
С некоторым упрощением Boost использует конструкцию следующим образом:
template <typename U>
char is_class_tester (void (U::*)(void));
template <typename U>
TypeBiggerThanChar is_class_tester (...);
template <typename T>
struct IsClass {
static const bool value = sizeof (is_class_tester<T>(0)) == 1;
};
Некоторые наблюдения:
- Эти шаблоны функций на самом деле не являются шаблонами функций.Это просто предварительные объявления пары перегруженных шаблонов функций.Сами шаблоны функций никогда не определяются.Понимание того, что это так, и понимание того, как это работает без необходимости когда-либо определять шаблоны, является одним из ключевых элементов в понимании этой конструкции.
- Ответы, в которых говорилось о том, как использовать этот первый шаблон функции, не попали в лодку.,Вы не можете использовать этот шаблон функции, потому что его определение не существует.
- Обратите внимание, что благодаря этому загадочному аргументу первый из двух шаблонов функций имеет смысл только тогда, когда тип
T
является классом.Фундаментальные типы и указатели не имеют функций-членов.Первое объявление является недопустимым синтаксисом для типов, не относящихся к классу. - Сравните это со вторым из перегруженных шаблонов функций, который является допустимым синтаксисом для всех параметров шаблона, и функция (если она существует) будет принимать любые аргументыброшен на него благодаря его ... аргумент.(В сторону: это смутно напоминает мою любимую однострочную программу на C, которая может решить любую проблему в мире при правильно отформатированном пользовательском вводе.)
- Хотя объявления шаблонов функций не могут использоваться как функции,объявления могут использоваться в простых запросах времени компиляции, таких как запрос о типе возвращаемого значения.Фактическое определение не требуется для этого вида запроса.Требуется только прототип.
- Это именно то, что шаблон класса
IsClass
делает для определения постоянной времени компиляции IsClass<SomeType>::value
.
Так как же получается IsClass<SomeType>::value
его ценность, и как он это делает во время компиляции?Компилятор должен либо разобраться в sizeof (is_class_tester<T>(0))
, либо отказаться от попыток.Нам нужно рассмотреть два случая на основе того, является ли тип SomeType
классом или нет.
Случай 1: SomeType
является классом.
Здесь оба объявления шаблона являются допустимым синтаксисом, поэтомуу компилятора есть два жизнеспособных кандидата на выбор.Компилятор может и должен использовать первый шаблон функции, потому что правила выбора диктуют, чтобы функция с переменным приоритетом имела самый низкий приоритет при выборе.Эта выбранная функция возвращает символ.Поскольку sizeof (char) гарантированно будет равен 1, IsClass<SomeType>::value
будет истинным в случае, если SomeType
является классом.
Случай 2: SomeType
не является классом.
Здесьгде начинается SFINAE. Первое объявление шаблона функции имеет неверный синтаксис.Компилятор не может просто сдаться здесь из-за SFINAE.Он должен продолжать искать альтернативу, и второе объявление шаблона функции отвечает всем требованиям.Единственная жизнеспособная функция возвращает TypeBiggerThanChar
, определение исключено, но, надеюсь, очевидно.Все, что нам нужно, это sizeof ().Он больше чем символ, поэтому IsClass<SomeType>::value
будет ложным в случае, если SomeType
не класс.