Что означает void (U :: *) (void)? - PullRequest
6 голосов
/ 19 июля 2010

Я смотрел на реализацию шаблона is_class в Boost и столкнулся с некоторым синтаксисом, который я не могу легко расшифровать.

    template <class U> static ::boost::type_traits::yes_type is_class_tester(void(U::*)(void));
    template <class U> static ::boost::type_traits::no_type is_class_tester(...);

Как мне интерпретировать void(U::*)(void) выше?Я знаком с C, поэтому он выглядит несколько аналогично void(*)(void), но я не понимаю, как U:: изменяет указатель.Кто-нибудь может помочь?

Спасибо

Ответы [ 5 ]

13 голосов
/ 19 июля 2010

* указывает на указатель, потому что вы можете получить доступ к его содержимому, написав *p. U::* указывает на указатель на член класса U. Вы можете получить доступ к его содержимому, написав u.*p или pu->*p (где u является экземпляром U).

Итак, в вашем примере void (U::*)(void) - это указатель на член U, который не принимает аргументов и не возвращает значения.

Пример:

class C { void foo() {} };

typedef void (C::*c_func_ptr)(void);

c_func_ptr myPointer = &C::foo;
3 голосов
/ 19 июля 2010

Это указатель на функцию-член класса U. Он очень похож на

void (*) (void)

, но указывает на функцию-член класса U.

3 голосов
/ 19 июля 2010

Вы правы, это аналог указателя на функцию. Скорее, это указатель на функцию-член, где член имеет класс U.

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

struct foo
{
    void bar(void);
};

void(*)(void) не подойдет, так как у него нет способа передать экземпляр класса. Скорее нам нужно:

void (foo::*)(void)

Указывает, что для этого указателя функции требуется экземпляр foo.


Для чего это стоит, вы используете их так:

typedef void (foo::*func_ptr)(void);

foo f;
foo* fp = &f;
func_ptr func = &foo::bar;

(f.*func)();
(fp->*func)();
3 голосов
/ 19 июля 2010

Начните с U, затем работайте наизнанку.

Объявленный тип является указателем на функцию-член из класса U, которая принимает аргументы void и возвращает void.

0 голосов
/ 02 июля 2011

Я собирался задать тот же самый вопрос, но он уже был задан.К сожалению, ни один из существующих ответов действительно не ответил на вопрос.По крайней мере, не для меня.Я должен был разгадать это.Я хотел задать тот же вопрос, что и ОП, плюс несколько.Мой вопрос: 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;
};

Некоторые наблюдения:

  1. Эти шаблоны функций на самом деле не являются шаблонами функций.Это просто предварительные объявления пары перегруженных шаблонов функций.Сами шаблоны функций никогда не определяются.Понимание того, что это так, и понимание того, как это работает без необходимости когда-либо определять шаблоны, является одним из ключевых элементов в понимании этой конструкции.
  2. Ответы, в которых говорилось о том, как использовать этот первый шаблон функции, не попали в лодку.,Вы не можете использовать этот шаблон функции, потому что его определение не существует.
  3. Обратите внимание, что благодаря этому загадочному аргументу первый из двух шаблонов функций имеет смысл только тогда, когда тип T является классом.Фундаментальные типы и указатели не имеют функций-членов.Первое объявление является недопустимым синтаксисом для типов, не относящихся к классу.
  4. Сравните это со вторым из перегруженных шаблонов функций, который является допустимым синтаксисом для всех параметров шаблона, и функция (если она существует) будет принимать любые аргументыброшен на него благодаря его ... аргумент.(В сторону: это смутно напоминает мою любимую однострочную программу на C, которая может решить любую проблему в мире при правильно отформатированном пользовательском вводе.)
  5. Хотя объявления шаблонов функций не могут использоваться как функции,объявления могут использоваться в простых запросах времени компиляции, таких как запрос о типе возвращаемого значения.Фактическое определение не требуется для этого вида запроса.Требуется только прототип.
  6. Это именно то, что шаблон класса 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 не класс.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...