Форум программистов, компьютерный форум CyberForum.ru
Наши страницы

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
 
Рейтинг: Рейтинг темы: голосов - 16, средняя оценка - 4.69
LosAngeles
Заблокирован
#1

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

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

столкнулся со странной конструкцией
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)
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
30.07.2011, 17:28
Здравствуйте! Я подобрал для вас темы с ответами на вопрос непонятная конструкция, шаблоны (C++):

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

«Шаблоны шаблонов» vs «шаблоны с параметрами-шаблонами». - C++
«Шаблоны шаблонов» vs «шаблоны с параметрами-шаблонами». Есть ли разница в этих понятиях? Если есть, то в чём? И где (в каких...

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

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

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

конструкция - C++
Доброго времени суток, с с++ мало знаком не могли бы пояснить некоторые моменты кода. #include &lt;math.h&gt; void tred2(float **a,...

Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
asics
Freelance
Эксперт С++
2847 / 1784 / 144
Регистрация: 09.09.2010
Сообщений: 3,841
30.07.2011, 17:36 #2
Из Шилдта:
Как правило, операторы, связаные с указателями на члены класса, применяються в исключительных ситуациях. В повседневном программирование они обычно не используються.
Я это к тому, что пока тебе не попадется вот такая "исключительная" ситуация, ты и не поймешь для чего они.(имхо, конечно)
1
LosAngeles
Заблокирован
30.07.2011, 17:40  [ТС] #3
а это какая именно из его книг(и глава желательно). Там подробно написано про это явление? Хотелось бы знать, что это за зверь ну на случай, вдруг если сам захочу придумать "исключительную ситуацию"
0
ValeryLaptev
Эксперт С++
1041 / 820 / 48
Регистрация: 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
asics
Freelance
Эксперт С++
2847 / 1784 / 144
Регистрация: 09.09.2010
Сообщений: 3,841
30.07.2011, 17:48 #5
Герберт Шилдт - Полный справочник по C++.
Цитата Сообщение от LosAngeles Посмотреть сообщение
Там подробно написано про это явление?
К сожелению, не совсем, автор просто дает понять что это такое, не углубляясь.
1
LosAngeles
Заблокирован
30.07.2011, 18:16  [ТС] #6
ValeryLaptev, спасибо огромное! грамотно всё разложил. Такой бы пост надо в какой-нибудь faq затолкать. Я думал, что знаю о указателях всё до твоего поста оказывается нет...
0
alex_x_x
бжни
2447 / 1652 / 84
Регистрация: 14.05.2009
Сообщений: 7,162
30.07.2011, 18:21 #7
можно радоваться, что это уйдет в прошлое с появлением std::function в стандарте
0
ValeryLaptev
Эксперт С++
1041 / 820 / 48
Регистрация: 30.04.2011
Сообщений: 1,659
30.07.2011, 18:30 #8
Цитата Сообщение от LosAngeles Посмотреть сообщение
ValeryLaptev, спасибо огромное! грамотно всё разложил. Такой бы пост надо в какой-нибудь faq затолкать. Я думал, что знаю о указателях всё до твоего поста оказывается нет...
В принципе, если админы-модераторы поддержат, то можно много чего наверху закрепить...
0
Deviaphan
Делаю внезапно и красиво
Эксперт C++
1287 / 1221 / 50
Регистрация: 22.03.2011
Сообщений: 3,744
30.07.2011, 19:45 #9
Цитата Сообщение от ValeryLaptev Посмотреть сообщение
В принципе, если админы-модераторы поддержат, то можно много чего наверху закрепить...
Только вот у ТС отсутствует список аргументов, при объявлении указателя на метод... Разве так можно?
0
LosAngeles
Заблокирован
30.07.2011, 19:56  [ТС] #10
вроде как можно. В g++ работаеет
0
Deviaphan
Делаю внезапно и красиво
Эксперт C++
1287 / 1221 / 50
Регистрация: 22.03.2011
Сообщений: 3,744
30.07.2011, 19:57 #11
Цитата Сообщение от LosAngeles Посмотреть сообщение
роде как можно.
Т.е. это указатель на любой метод класса?
0
Kastaneda
Форумчанин
Эксперт С++
4653 / 2862 / 228
Регистрация: 12.12.2009
Сообщений: 7,271
Записей в блоге: 2
Завершенные тесты: 1
30.07.2011, 20:08 #12
Цитата Сообщение от LosAngeles Посмотреть сообщение
C++
1
template<typename C> static One test(int C::*);
По-моему это все таки указатель на переменную класса.
0
LosAngeles
Заблокирован
30.07.2011, 20:25  [ТС] #13
Цитата Сообщение от Deviaphan Посмотреть сообщение
Т.е. это указатель на любой метод класса?
наверно так, если такая терминология уместна. Наверно это неявно преобразуется в int (C::*p)(void) или что-то типа того универсальных указателей ведь нет? Кстати если в FAQ это добавлять надо ещё добавить что спецификатор const обязателен, если метод был с ним объявлен, а спецификация исключений вроде необязательна должна совпадать, у меня по крайней мере так
0
Kastaneda
Форумчанин
Эксперт С++
4653 / 2862 / 228
Регистрация: 12.12.2009
Сообщений: 7,271
Записей в блоге: 2
Завершенные тесты: 1
30.07.2011, 20:40 #14
Цитата Сообщение от LosAngeles Посмотреть сообщение
Наверно это неявно преобразуется в int (C::*p)(void) или что-то типа того
Нет, не может такого быть.
тык, это разные типы.
1
LosAngeles
Заблокирован
30.07.2011, 20:47  [ТС] #15
а что тогда можно присвоить р?
0
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
30.07.2011, 20:47
Привет! Вот еще темы с ответами:

Конструкция - C++
ребят помогите разобрать конструкцию вида (ah==72)?k--:k; знаю что чтото логическое но вспомнить немогу:)

Конструкция if.. - C++
Помогите пожалуйста довести задачу до конца! Дано натуральное число N. Если оно делится на 4, вывести на экран ответ N=4k (где k –...

Циклическая конструкция - C++
Не могу составить цикл для S = cos1*(cos1 + cos 2) * ... *(cos1+ cos 2 + ... + cos n)

Не работает конструкция с new - C++
char**ex=new char*; for(i=0;i&lt;n;i++) ex=new char; for(i=0;i&lt;n;i++) cin&gt;&gt;ex; почему не работает


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

Или воспользуйтесь поиском по форуму:
Yandex
Объявления
30.07.2011, 20:47
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru