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

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

Войти
Регистрация
Восстановить пароль
 
MrAndrey_ka
78 / 78 / 2
Регистрация: 13.05.2009
Сообщений: 537
Записей в блоге: 1
#1

Обход константности переменной - C++

11.04.2014, 16:21. Просмотров 585. Ответов 11

в классе есть метод с разными параметрами:
C++
1
2
3
4
5
6
7
8
    int Find(const WCHAR* Val, size_t Beg = 0, size_t Len = 0){
        ...
        };
 
    template <class Type2>
    int Find(const Type2 Val, size_t Beg = 0, size_t Len = 0){
        ...
        };
вот так вызывается как следует:
C++
1
2
3
A.Find(25);
A.Find(2.5);
A.Find(L"sd");
а вот так:
C++
1
2
    WCHAR * H = L"sd";
    A.Find(H, 0, 2);
вызывается метод template <class Type2> int Find(const Type2 Val, size_t Beg = 0, size_t Len = 0)
хотя по всей логике должен вызваться int Find(const WCHAR* Val, size_t Beg = 0, size_t Len = 0), как это обойти не добавляя еще и такое объявления метода int Find(WCHAR* Val, size_t Beg = 0, size_t Len = 0)???
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
11.04.2014, 16:21
Здравствуйте! Я подобрал для вас темы с ответами на вопрос Обход константности переменной (C++):

Время приобретения объектом константности - C++
Всем привет Есть такая ситуация: // Функция: void someFunc(const std::vector&lt;MyObj&gt; objects); std::vector&lt;MyObj&gt;...

Почему компилятор требует константности для заданного метода? - C++
Приветствую всех! Имеется класс Fraction с перегруженными операциями сложения, вычитания, умножения и деления. Перегруженные операции...

Разница между понятиями "Обход в прямом направлении" и "Итерационный прямой обход" - C++
Ребятаа, обьясните чем различается: Обход в прямом направлении и Итерационный прямой обход Добавлено через 10 минут НароооД,...

a,b и c.Присвоить максимальное из них переменной a,минимальное-переменной c,среднее переменной b - C++
даны произвольные числа a,b и c.Присвоить максимальное из них переменной a,минимальное-переменной c,среднее переменной b.

Присвоить значение наименьшего элемента массива переменной М1, номер строки, где находится этот элемент, - переменной Т, номер столбца - переменной С - C++
Массив С действительных чисел имеет 5 строк и 11 столбцов. Присвоить значение наименьшего элемента массива переменной М1, номер строки, где...

Переменной d присвоить первую цифру после запятой некоторой переменной x типа float - C++
Нужно целой переменной d присвоить первую цыфру после комы некоторой переменной x типа float Скажите, пожалуйста, с чего начать и по...

11
aLarman
644 / 565 / 89
Регистрация: 13.12.2012
Сообщений: 2,109
Завершенные тесты: 1
11.04.2014, 16:24 #2
C++
1
2
const WCHAR * H = L"sd";
A.Find(H, 0, 2);
?
0
Ilot
Модератор
Эксперт С++
1825 / 1183 / 232
Регистрация: 16.05.2013
Сообщений: 3,119
Записей в блоге: 5
Завершенные тесты: 1
11.04.2014, 16:27 #3
На самом то деле вот это:
C++
1
WCHAR * H = L"sd";
это
C++
1
WCHAR * const H...
если я не ошибаюсь.
0
MrAndrey_ka
78 / 78 / 2
Регистрация: 13.05.2009
Сообщений: 537
Записей в блоге: 1
11.04.2014, 16:51  [ТС] #4
вы не поняли, мне не нужно подстраивать вызов под класс, я хочу подстроить класс, классом будут пользоваться люди или даже я забуду тонкости такого вызова, корректно работает и в том случае и в том, но если вызвать template <class Type2> int Find(const Type2 Val, size_t Beg = 0, size_t Len = 0), то будет выделена память и банально скопирована строка с одного места в другое, что явно лишнее.
0
Nick Alte
Эксперт С++
1644 / 1016 / 120
Регистрация: 27.09.2009
Сообщений: 1,945
Завершенные тесты: 1
11.04.2014, 17:24 #5
У строковых констант тип не const char* (на разнице между char и wchar внимание не заостряем). У них тип массива const char с конкретным размером, который легко и просто превращается в const char *. А вот в шаблоне такого превращения не будет, шаблон будет создан для точного типа. Так что понадобится ещё и такая перегрузка:
C++
1
2
template <size_t N>
int Find(const WCHAR (&Val)[N], size_t Beg = 0, size_t Len = 0)
0
MrAndrey_ka
78 / 78 / 2
Регистрация: 13.05.2009
Сообщений: 537
Записей в блоге: 1
11.04.2014, 17:39  [ТС] #6
Nick Alte и как этим чудом пользоваться, дописал твою предложение но его не вызвала ни
WCHAR * H = L"sd";
A.Find(H, 0, 2);
ни
A.Find( L"sd", 0, 2);

У них тип массива const char с конкретным размером, который легко и просто превращается в const char *
ты хочешь сказать что следующие 4 байта после указателя содержат его размер?

Добавлено через 4 минуты
следующие 4 байта после указателя содержат его размер
проверил это полый бред!
0
DrOffset
7506 / 4502 / 1023
Регистрация: 30.01.2014
Сообщений: 7,362
12.04.2014, 01:11 #7
Цитата Сообщение от MrAndrey_ka Посмотреть сообщение
ты хочешь сказать что следующие 4 байта после указателя содержат его размер?
Нет, он не об этом. Он говорит о том, что строковый литерал L"test string" имеет тип const wchar_t[12]. Видишь const, он там не случайно. Строка такая размещается обычно в сегменте констант и менять ее нельзя.
Теперь про указатели и массивы. Как видно из типа, это константный массив. Но массивы в С и С++ это не объекты первого класса, их нельзя копировать. Это такая абстракция для представления памяти, но настоящего объекта со свойствами присваиваемости, копируемости - нет. Именно поэтому есть всякие функции типа strcpy.
В общем в силу этих причин всякий раз когда мы передаем наш массив куда-то он неявно преобразуется к указателю. В языке Си было можно неявно снимать const при таком преобразовании, а вот в С++ нельзя! То есть если мы имели константный массив, то в таком, например, коде:
C++
1
wchar_t * p = "test string";
выполняется неявный каст массива к указателю и неявное снятие константности. В С++ такой код некорректен. Но, он же работает! Почему? Объясняю. С++ наследник С, в первую очередь в плане исходных кодов, поэтому очень много С кода стало разваливаться (переставать собираться компилятором С++) из-за таких вот приемов. Поэтому многие разработчики компиляторов просто пошли на уступки и не стали делать эту ситуацию ошибкой. GCC например выдает такое предупреждение:
warning: deprecated conversion from string constant to 'wchar_t*' [-Wwrite-strings]
Теперь к твоей проблеме. Т.к. у тебя тип без константности, а прототип функции с константностью, то при поиске подходящей перегрузки отдается предпочтение шаблону. Почему? Объясняю. Выбор происходит исходя из наилучшего соответствия. В нешаблонной функции стоит const, который говорит, что данные по указателю будут считаться константами. Наш указатель, утверждает, что данные неконстантны (хотя это на самом деле не так, как я выше объяснял). Следовательно здесь необходимо сделать разрешенный правилами языка неявный каст wchar_t * -> const wchar_t *. Теперь смотрим шаблонную функцию: const там применяется ко всему типу, то есть в этом случае мы получим константный указатель (wchar_t * -> wchar_t * const). Отличие здесь в уровне константности. То есть в шаблонном варианте так называемая qualification conversion применяется к первому уровню (т.е. к самому типу), а во втором, нешаблонном варианте ко второму уровню (т.е. к указываемому типу). Исходя из этого и выбирается шаблонная функция.
Вообще советую очень вдумчиво прочитать параграф 13.3.3.1.1 стандарта, и в частности параграф 4.4. Эта тема не очень простая и для того, чтобы больше так не встревать, ее надо бы хорошо разобрать.
Ну а пример твой лечится исправлением кода в соответствии со стандартом (уже постил aLarman):
C++
1
2
const WCHAR * H = L"sd";
A.Find(H, 0, 2);
Здесь мы вообще убрали необходимость кастов, поэтому выбралась нешаблонная функция. Потому что всегда, когда есть полностью подходящая нешаблонная функция, шаблонная не будет вызываться.
По поводу перегрузки советую заглянуть в раздел 13.3.
3
Tulosba
:)
Эксперт С++
4619 / 3236 / 297
Регистрация: 19.02.2013
Сообщений: 9,045
12.04.2014, 10:19 #8
DrOffset, хороший ответ. Но мне кажется, не хватает разъяснений почему преобразование
T * -> T * const
преобладает над преобразованием
T * -> const T *
. Т.е. про уровни конечно сказано, но что кроется за первым, вторым (прочими?) уровнями не совсем ясно. Они описаны в стандарте именно как уровни или как-то иначе?
Цитата Сообщение от DrOffset Посмотреть сообщение
Ну а пример твой лечится исправлением кода в соответствии со стандартом
Предположу, что ТС хотел бы видеть вариант когда не пользователю приходится подгонять тип к функции, а наоборот, подогнать функцию к аргументу. Т.е. нужно замутить еще одну перегрузку:
C++
1
2
int Find(WCHAR* Val, size_t Beg = 0, size_t Len = 0) // убрали const
{   ...  };
0
DrOffset
7506 / 4502 / 1023
Регистрация: 30.01.2014
Сообщений: 7,362
12.04.2014, 13:30 #9
Цитата Сообщение от Tulosba Посмотреть сообщение
Они описаны в стандарте именно как уровни или как-то иначе?
Именно как уровни. В стандарте есть понятие multi-level pointer.
Но здесь даже не это играет роль, я действительно вчера немного неточно выразился.
По стандарту отдается приоритет в первую очередь, если типы - равны, и отличаются только cv-квалификаторами.
S1 and S2 differ only in their qualification conversion and yield similar types T1 and T2 (4.4),
respectively, and the cv-qualification signature of type T1 is a proper subset of the cv-qualification
signature of type T2. [Example:
int f(const int *);
int f(int *);
int i;
int j = f(&i); // cal ls f(int*)
В записи T * и T * const - T - это один и тот же тип. А вот в этой записи: T * и T const * - типы разные. (T и T const) Во второй записи придется делать преобразование (qualification conversion) типа указываемого с учетом уровня(pointer level) косвенности указателя. А в первой - только qualification conversion самого типа указателя. В стандарте есть понятие rank преобразования, от которого зависит выбор best viable function при перегрузке. Но простой таблицы, которую можно было бы показать новичку - нет (на самом деле есть небольшая таблица, но она отражает ранжировку по типам преобразования, но у нас вся ситуация внутри одного типа - qualification conversion). Но таблица выводится, если прочитать вышеупомянутые параграфы. Именно после полного прочтения составляется цельная картина, поэтому я не стал сюда цитировать такой большой объем текста, а просто отправил на нужные пункты.
1
Tulosba
:)
Эксперт С++
4619 / 3236 / 297
Регистрация: 19.02.2013
Сообщений: 9,045
12.04.2014, 13:41 #10
Цитата Сообщение от DrOffset Посмотреть сообщение
я действительно вчера немного неточно выразился.
Спишем это на влияние вечера пятницы.
0
MrAndrey_ka
78 / 78 / 2
Регистрация: 13.05.2009
Сообщений: 537
Записей в блоге: 1
14.04.2014, 11:25  [ТС] #11
Цитата Сообщение от Tulosba Посмотреть сообщение
Предположу, что ТС хотел бы видеть вариант когда не пользователю приходится подгонять тип к функции, а наоборот, подогнать функцию к аргументу. Т.е. нужно замутить еще одну перегрузку:
Код C++
1
2
int Find(WCHAR* Val, size_t Beg = 0, size_t Len = 0) // убрали const
{ * ... *};
Совершенно верно, о чем я и писал во втором своем сообщении, да и в первом тоже... но на сколько я понимаю без лишней перезагрузки никак!

Раз уж тема зашла в разбор константности задам еще один вопрос:
Была идея просто написать шаблонную функцию а уже в ней проверять тип передаваемого параметра, и плясать уже в зависимости от этого. Проверял я так
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template <class T1, class T2>
struct same_type {
    enum { val = 0 };
    };
 
template<class T>
struct same_type<const T, const T> {
    enum { val = 1 };
    };
 
  template <class Type2>
    int Find(const Type2 Val, size_t Beg = 0, size_t Len = 0){
        if(same_type<Type2, WCHAR*>::val == 1)
{...}
else{...}
        };
Но и тут проблема с константностью, const WCHAR* И WCHAR*не возвращает 1,
пробовал писать if(same_type<const Type2 const, const WCHAR* const>::val == 1), но если Type2 это const WCHAR выдает предупреждение "повторное использование const". Что можете посоветовать в этом случае?
0
DrOffset
7506 / 4502 / 1023
Регистрация: 30.01.2014
Сообщений: 7,362
14.04.2014, 12:05 #12
Цитата Сообщение от MrAndrey_ka Посмотреть сообщение
Что можете посоветовать в этом случае?
Если я тебя верно понял, то так:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template <class T1, class T2>
struct same_type
{
    enum { val = 0 };
};
 
template<class T>
struct same_type<T const *, T *>
{
    enum { val = 1 };
};
 
template<class T>
struct same_type<T *, T *>
{
    enum { val = 1 };
};
Но с перегрузкой вариант все же лучше.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int Find(WCHAR const * Val, size_t Beg = 0, size_t Len = 0)
{
    return find_impl(Val, Beg, Len);
}
int Find(WCHAR * Val, size_t Beg = 0, size_t Len = 0)
{
    // find_impl - реализация для WCHAR
    return find_impl(Val, Beg, Len);
}
 
template <class Type2>
int Find(const Type2 Val, size_t Beg = 0, size_t Len = 0)
{
    // шаблонна реализация
}
// private
int find_impl(WCHAR const * Val, size_t Beg, size_t Len)
{
    // реализация для WCHAR
}
1
14.04.2014, 12:05
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
14.04.2014, 12:05
Привет! Вот еще темы с ответами:

Получить доступ к переменной класса при наличии такого же имени переменной в функции - C++
Например, есть приватная переменная clientName в классе. Также есть точно такая же переменная в конструкторе этого класса. И мне нужно...

Часть имени переменной как значние другой переменной - C++
Нужно чтобы имя переменной состояло как бы из двух частей к примеру переменную x1 надо записать так чтобы число 1 было в другой...

Создать две переменных, ввести их с клавиатуры. Вывести строки: имя переменной - адрес переменной - значение п - C++
Создать две переменных, ввести их с клавиатуры. Вывести строки: имя переменной - адрес переменной - значение переменной.

Присвоить переменной char m значиние переменной int i.. - C++
Как присвоить переменной char m значиние переменной int i таким образом ? int main() { char m; int i = 5; m = i; # ( что бы...


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

Или воспользуйтесь поиском по форуму:
12
Ответ Создать тему
Опции темы

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