Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.80/15: Рейтинг темы: голосов - 15, средняя оценка - 4.80
Заблокирован
1

непонятная конструкция, шаблоны

30.07.2011, 17:28. Показов 3107. Ответов 38
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
столкнулся со странной конструкцией
C++
1
template<typename C> static One test(int C::*);
в
C++
1
2
3
4
5
6
7
8
9
10
11
12
template<typename T>
class IsClassT {
  private:
    typedef char One;
    typedef struct { char a[2]; } Two;
    template<typename C> static One test(int C::*);
    
    template<typename C> static Two test(...);
  public:
    enum { Yes = sizeof(IsClassT<T>::test<T>(0)) == 1 };
    enum { No = !Yes };
};
в упор не понимаю, что значит int C::*
видел объяснения на русском, на инглише, что это мол какой-то указатель на член, всё равно не догоняю. Может кто-нибудь доходчиво объяснить или хотя привести примеры зачем это нужно ну кроме как здесь? Всё остальное понятно, мне бы этот момент главное прояснить. Заранее спасибо
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
30.07.2011, 17:28
Ответы с готовыми решениями:

Шаблоны классов: непонятная ошибка в одном из методов класса
Задача создать шаблон двоичного дерева поиска. В методе удаления узла IntelliSense выдает ошибку:...

Непонятная конструкция в C++
Наткнулся на конструкцию, для которой g++ работает не так, как компилятор безо всяких расширений (в...

«Шаблоны шаблонов» vs «шаблоны с параметрами-шаблонами».
«Шаблоны шаблонов» vs «шаблоны с параметрами-шаблонами». Есть ли разница в этих понятиях? Если...

Шаблоны. Плохо понимаемые моменты из книги "Шаблоны С++. Справочник разработчика". (Вандевурд, Джосаттис)
Так как изучаю эту книгу, то в некоторых местах возникают вопросы. Чтобы не плодить много тем,...

38
Freelance
Эксперт С++
2891 / 1826 / 356
Регистрация: 09.09.2010
Сообщений: 3,841
30.07.2011, 17:36 2
Из Шилдта:
Как правило, операторы, связаные с указателями на члены класса, применяються в исключительных ситуациях. В повседневном программирование они обычно не используються.
Я это к тому, что пока тебе не попадется вот такая "исключительная" ситуация, ты и не поймешь для чего они.(имхо, конечно)
1
Заблокирован
30.07.2011, 17:40  [ТС] 3
а это какая именно из его книг(и глава желательно). Там подробно написано про это явление? Хотелось бы знать, что это за зверь ну на случай, вдруг если сам захочу придумать "исключительную ситуацию"
0
Эксперт С++
1069 / 848 / 60
Регистрация: 30.04.2011
Сообщений: 1,659
30.07.2011, 17:48 4
Лучший ответ Сообщение было отмечено как решение

Решение

LosAngeles, смотри сюда:


Дело в том, что указатель на метод существенно отличается по типу от обычного указателя на функцию, даже если прототипы функции и метода внешне идентичны. Вспомним, как определяется указатель на функцию
C++
1
тип (*имя-указателя)(спецификация параметров);
Для сокращения записи применяют обычно оператор typedef. Объявление
C++
1
typedef тип (*тип-указателя)(спецификация параметров);
вводит новое имя типа. После этого указатель на функцию можно объявлять как
C++
1
тип-указателя имя-указателя;
Указателю присваивается адрес функции. В С++ разрешается присваивать адрес функции
либо явно задавая операцию получения адреса,
C++
1
имя-указателя = &имя-функции;
либо неявно
C++
1
имя-указателя = имя-функции;
В последнем случае имя функции неявно преобразуется в адрес.

Вызов функции через указатель выполняется опять же либо так:
C++
1
(*имя-указателя)(аргументы);
либо в более простой форме,
C++
1
имя-указателя(аргументы);
которая эквивалентна предыдущей
Рассмотрим несколько простых примеров. Допустим, у нас объявлен указатель на функцию
C++
1
int (*pf)(void);
Этому указателю можно присвоить адрес любой функции с таким же прототипом, в том числе и библиотечной. Например, такой прототип имеет функция-генератор случайного числа rand(), который прописан в заголовке библиотеки <cstdlib>.
C++
1
2
3
4
5
int f1(void) { return 1; } 
extern “C” int f2(void) { return 2; }
pf = f1;    cout << pf() << endl;               // вызов f1()
pf = f2;    cout << pf() << endl;               // вызов f2()
pf = &rand; cout << pf() << endl;               // вызов rand()
Как видите, спецификация компоновки никак не мешает косвенному вызову. Напишем теперь простой класс
C++
1
2
3
4
5
6
class Constant
{   int value;
   public: 
    Constant(const int &a): value(a) {}
    int get(void) { return value; }
};
Метод get() с виду имеет тот же прототип, что и функции в приведенном выше фрагменте. Однако попытки присвоить адрес метода get() указателю pf компилятор пресекает «на корню». Да и не совсем понятно, как этот адрес задавать. Вариантов два:
 объявить объект и взять адрес метода в объекте;
 не объявлять объект, а приписать префикс класса.
C++
1
2
3
4
// первый вариант
Constant A(5); pf = &A.get();               
// второй вариант
pf = &Constant::get;
Первый вариант вообще неверный: и Visual C++.NET 2003, и Borland C++ Builder 6 сообщают, что операцию взятия адреса & так использовать нельзя. Второй вариант – ближе к истине: оба компилятора сообщают только о невозможности выполнить преобразование типов. Попытки прописать преобразование явно — не проходят.
C++
1
2
3
pf = static_cast<int (*)(void)>(&Constant::get);
pf = reinterpret_cast<int (*)(void)>(&Constant::get); 
pf = (int (*)(void))(&Constant::get);
Все эти варианты вызывают ту же ошибку компиляции — невозможность преобразования типов. Таким образом, тип указателя на метод класса кардинально отличается от типа указателя на функцию: адрес метода нельзя присвоить указателю на функцию, даже если внешне их прототипы совпадают. Это становится понятным, если мы вспомним, что нестатические методы получают дополнительный параметр — указатель this.

Аналогично, нельзя присвоить обычному указателю на функцию адрес виртуального метода — в этом случае дело усугубляется еще наличием в составе объекта указателя на таблицу виртуальных методов. А вот со статическими методами картина другая! Статический метод не получает никаких «лишних параметров», поэтому его адрес можно присваивать обычному указателю на функцию без всяких преобразований. Добавим в класс Constant статическое поле и статический метод с нужным нам прототипом:
C++
1
2
3
4
5
6
7
8
class Constant
{   int value;
    static int d;
public: 
    Constant(const int &a): value(a) {}
    int get(void) { return value; }
    static int getd(void) { return d; } // --статический метод
};
Тогда нашему указателю pf можно присвоить адрес статического метода вторым из приведенных выше способов, например:
C++
1
pf = Constant::getd;        // или pf = &Constant::getd;
Префикс, естественно, необходимо писать.
Указатель на метод объявляется по-другому [1-8.3.3] — нужно задать префикс-имя класса:
C++
1
int (Constant::*pm)(void);
Такому указателю можно присваивать адреса обычных и виртуальных методов вторым их показанных выше способов, например
C++
1
pm = &Constant::get;        // или pm = Constant::get;
Адрес статического метода, так же как и адрес обычной функции, такому указателю присвоить нельзя — возникает ошибка трансляции: компилятор сообщает о невозможности выполнить преобразование типов.
И косвенный вызов метода выполняется по-другому — с помощью операции
выбора члена класса .* или ->* [1-5.5].
Несмотря на то, что указатель на член класса является отдельной, независимой от класса переменной, вызов метода по указателю возможен только при наличии объекта, например
C++
1
2
Constant A(5);
cout << (A.*pm)() << endl;
Выражение(A.*pm)() означает следующее: для объекта A вызвать метод, чей адрес записан в указателе pm. Слева от операции .* — объект, справа — указатель на метод. Скобки вокруг выражения A.*pm писать обязательно, так как приоритет операции () вызова функции выше, чем приоритет операции выбора члена класса .*.

В выражении (объект.*указатель) можно заменить часть «объект.» на «указатель->», например
C++
1
2
Constant *pc = new Constant(7);
cout << (pc->*pm)() << endl;
Обратите внимание: в выражении (pc->*pm) слева — обычный указатель на динамический объект, а справа — указатель на метод этого объекта. Это совершенно два разных типа указателя.
Интересно, что в системе Visual C++.NET 2003 размер sizeof(pf) указателя на функцию совпадает с размером sizeof(pm) указателя на член класса и равен 4 байта. А вот система Borland C++ Builder 6 выдает совершенно разные цифры: размер указателя на функцию равен 4 байтам, а размер указателя на метод — 12 байт!
11
Freelance
Эксперт С++
2891 / 1826 / 356
Регистрация: 09.09.2010
Сообщений: 3,841
30.07.2011, 17:48 5
Герберт Шилдт - Полный справочник по C++.
Цитата Сообщение от LosAngeles Посмотреть сообщение
Там подробно написано про это явление?
К сожелению, не совсем, автор просто дает понять что это такое, не углубляясь.
1
Заблокирован
30.07.2011, 18:16  [ТС] 6
ValeryLaptev, спасибо огромное! грамотно всё разложил. Такой бы пост надо в какой-нибудь faq затолкать. Я думал, что знаю о указателях всё до твоего поста оказывается нет...
0
бжни
2473 / 1684 / 135
Регистрация: 14.05.2009
Сообщений: 7,162
30.07.2011, 18:21 7
можно радоваться, что это уйдет в прошлое с появлением std::function в стандарте
0
Эксперт С++
1069 / 848 / 60
Регистрация: 30.04.2011
Сообщений: 1,659
30.07.2011, 18:30 8
Цитата Сообщение от LosAngeles Посмотреть сообщение
ValeryLaptev, спасибо огромное! грамотно всё разложил. Такой бы пост надо в какой-нибудь faq затолкать. Я думал, что знаю о указателях всё до твоего поста оказывается нет...
В принципе, если админы-модераторы поддержат, то можно много чего наверху закрепить...
0
Делаю внезапно и красиво
Эксперт С++
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
30.07.2011, 19:45 9
Цитата Сообщение от ValeryLaptev Посмотреть сообщение
В принципе, если админы-модераторы поддержат, то можно много чего наверху закрепить...
Только вот у ТС отсутствует список аргументов, при объявлении указателя на метод... Разве так можно?
0
Заблокирован
30.07.2011, 19:56  [ТС] 10
вроде как можно. В g++ работаеет
0
Делаю внезапно и красиво
Эксперт С++
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
30.07.2011, 19:57 11
Цитата Сообщение от LosAngeles Посмотреть сообщение
роде как можно.
Т.е. это указатель на любой метод класса?
0
5231 / 3204 / 362
Регистрация: 12.12.2009
Сообщений: 8,135
Записей в блоге: 2
30.07.2011, 20:08 12
Цитата Сообщение от LosAngeles Посмотреть сообщение
C++
1
template<typename C> static One test(int C::*);
По-моему это все таки указатель на переменную класса.
0
Заблокирован
30.07.2011, 20:25  [ТС] 13
Цитата Сообщение от Deviaphan Посмотреть сообщение
Т.е. это указатель на любой метод класса?
наверно так, если такая терминология уместна. Наверно это неявно преобразуется в int (C::*p)(void) или что-то типа того универсальных указателей ведь нет? Кстати если в FAQ это добавлять надо ещё добавить что спецификатор const обязателен, если метод был с ним объявлен, а спецификация исключений вроде необязательна должна совпадать, у меня по крайней мере так
0
5231 / 3204 / 362
Регистрация: 12.12.2009
Сообщений: 8,135
Записей в блоге: 2
30.07.2011, 20:40 14
Цитата Сообщение от LosAngeles Посмотреть сообщение
Наверно это неявно преобразуется в int (C::*p)(void) или что-то типа того
Нет, не может такого быть.
тык, это разные типы.
1
Заблокирован
30.07.2011, 20:47  [ТС] 15
а что тогда можно присвоить р?
0
5231 / 3204 / 362
Регистрация: 12.12.2009
Сообщений: 8,135
Записей в блоге: 2
30.07.2011, 22:08 16
Цитата Сообщение от LosAngeles Посмотреть сообщение
а что тогда можно присвоить р?
Сижу, методом тыка пытаюсь определить - это вообще что?
Арифметические операции к этому p не применяются (ошибка компиляции), оператор разыменования указателя тоже. Присвоить какое-то значение ему невозможно, если вывести это p, то выводится просто 1. Пробую вызывать ф-цию по-разному, не указатель на int, не указатель на функцию типа int(*)() в качестве аргумента неприемлемы (ошибка компиляции). Но сама строка:
C++
1
int C::*p;
компилируется нормально. Вообще странно.
P.S. у меня C - это просто класс.

Добавлено через 2 минуты
Да, оператор () тоже неподходит...

Добавлено через 45 минут
LosAngeles, вот нашел в википедии:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  class MyClass
  {
  public:
    int a;
  };
 
  template< class T >
  T& IncrementIntegerElement( int T::* Element, T& Object )
  {
    Object.*Element += 1;
    return Object;
  }
 
  template< class T >
  T IncrementMyClassElement( T MyClass::* Element, MyClass& Object )
  {
    Object.*Element += 1;
    return Object.*Element;
  }
 
  MyClass Obj;
  int n;
 
  n = ( IncrementIntegerElement( &MyClass::a, Obj ) ).a;
  n = IncrementMyClassElement( &MyClass::a, Obj );
Как и предполагалось, это просто указатель на член класса типа int, просто похоже подобная конструкция используется только с шаблонами (с простым классом некомпилируется).
1
Заблокирован
31.07.2011, 06:17 17
Цитата Сообщение от Deviaphan Посмотреть сообщение
Только вот у ТС отсутствует список аргументов, при объявлении указателя на метод... Разве так можно?
2008 студия грязно выругалась.

А так, вапще, указатель на метод мне видится неким фейлом.
Попытка заюзать ООП в качестве функционального ЯП
0
Делаю внезапно и красиво
Эксперт С++
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
31.07.2011, 07:04 18
Цитата Сообщение от Bers Посмотреть сообщение
А так, вапще, указатель на метод мне видится неким фейлом.
Попытка заюзать ООП в качестве функционального ЯП
Не, не фэйл. Порекомендую ещё раз прочесть Александреску (если после прошлой рекомендации ещё не успели прочитать.) ) про функциональные объекты.


Цитата Сообщение от Kastaneda Посмотреть сообщение
Как и предполагалось, это просто указатель на член класса типа int,
Вероятно, так и есть. Но смысл действа не ясен. Почему там просто указатель на int не передаётся? Ничё не понятно... Если кто понял сакральный смысл этого действа, разъясните тугодуму, плиз.)
0
Заблокирован
31.07.2011, 07:15  [ТС] 19
если указатель на инт передавать, то первый вариант будет компилироваться всегда вне зависимости от Т вроде как
0
Делаю внезапно и красиво
Эксперт С++
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
31.07.2011, 07:19 20
Цитата Сообщение от LosAngeles Посмотреть сообщение
если указатель на инт передавать
Для второго варианта, указатель на Т передать.)
0
31.07.2011, 07:19
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
31.07.2011, 07:19
Помогаю со студенческими работами здесь

Помогите писать на С++ через шаблоны. Консуле я писал, но надо писать исползуя шаблоны
В одномерном массиве, состоящем из п вещественных элементов, вычислить: 1) количество элементов...

Непонятная конструкция
var { indexedDB } = require('sdk/indexed-db'); зачем операторные скобки при объявлении переменной?...

Непонятная конструкция
Сделал я вот такую штуку https://jsfiddle.net/BogdanHackerDeveloper/dzsr4d4x/ Но непонятна одна...

Непонятная конструкция
Объясните как понимать эту строку return ($a &lt; $b) ? -1 : 1; &lt;?php function cmp($a, $b) { ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru