LINUX.ORG.RU

Два метода, отличаются вроде бы только константностью

 ,


0

3

Привет, ЛОР.

В заголовочных файлах OpenCV нашёл занятное:

inline
uchar* Mat::ptr(int i0, int i1)
{
    CV_DbgAssert(dims >= 2);
    CV_DbgAssert(data);
    CV_DbgAssert((unsigned)i0 < (unsigned)size.p[0]);
    CV_DbgAssert((unsigned)i1 < (unsigned)size.p[1]);
    return data + i0 * step.p[0] + i1 * step.p[1];
}

inline
const uchar* Mat::ptr(int i0, int i1) const
{
    CV_DbgAssert(dims >= 2);
    CV_DbgAssert(data);
    CV_DbgAssert((unsigned)i0 < (unsigned)size.p[0]);
    CV_DbgAssert((unsigned)i1 < (unsigned)size.p[1]);
    return data + i0 * step.p[0] + i1 * step.p[1];
}

Вопрос: а как компилятор понимает, к какому из этих методов обращение? У них же номенклатура параметров идентична, разница только в const.

У меня, когда я попытался присвоить результат функции неконстантному указателю, компилятор ругнулся, мол, invalid conversion from ‘const uchar*’ {aka ‘const unsigned char*’} to ‘uchar*’ {aka ‘unsigned char*’}. А вот константному присвоилось нормально.

★★★★★

Вопрос: а как компилятор понимает, к какому из этих методов обращение?

Исключительно исходя из константности указателя ‘this’.

ПыСы. И в догонку: вдруг пригодится.

bugfixer ★★★★★
()
Последнее исправление: bugfixer (всего исправлений: 1)

По const-ности объекта, на котором метод вызывается. Его квалификация и категория значения тоже часть сигнатуры и важны при выборе перегрузки наравне с аргументами.

То же самое, что перегрузки вида

template<typename T>
struct Array {
  ...
  T& operator[](size_t index);
  const T& operator[](size_t index) const;
};

В C++23 это даже окончательно признали и большей частью убрали необходимость в дупликации кода (которая в некоторых случаях может быть далеко не двойная), а попутно решили ещё несколько проблем. См. deducing this.

lwo
()
Последнее исправление: lwo (всего исправлений: 1)

Всем спасибо за ответы, век живи – век учись. Я-то несмотря на почти 20 лет программирования на плюсах полагал, что если я такую перегрузку сделаю в своём коде (номенклатура параметров идентична), компилятор её просто откажется собирать. А тут – вот оно что…

hobbit ★★★★★
() автор топика
Последнее исправление: hobbit (всего исправлений: 1)
Ответ на: комментарий от hobbit

Я-то несмотря на почти 20 лет программирования на плюсах полагал, что если я такую перегрузку сделаю в своём коде, компилятор её просто откажется собирать

Но черт возьми, как? Как столько лет можно программировать на плюсах не имея представления о const-методах? Ведь даже у std::vector методы begin есть в const- и не-const вариантах

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Очевидно, потому, что векторами я просто пользуюсь, вот если я захочу написать свой вектор и проверить его на нескольких десятках разных use case — вот тогда, наверное, столкнусь с таким.

И что значит «не имея представления»? const-методы я и использую, и создаю при необходимости. А вот один и тот же метод в const- и не-const вариантах не создавал, даже не задумывался о такой возможности.

hobbit ★★★★★
() автор топика
Ответ на: комментарий от eao197

Удивляет другое. Когда таким образом самоутверждаются школьники - это понятно. Гормоны, стадное поведение, поиск места в социальной иерархии и всё такое. А когда этим занимаются старые пердуны вроде тебя - это уже совсем клиника.

anonymous
()
Ответ на: комментарий от anonymous

А когда этим занимаются старые пердуны вроде тебя - это уже совсем клиника.

Зря Вы так - я тоже был в растеренности. Возможно господин @hobbit настолько завязан на QTшные контейнеры что STL мало использует (хотя что там в QT происходит - я без руля: у нас он условно «забанен» в силу многих причин). А вот в STL есть гораздо более тонкие моменты, например std::set::iterator это на самом деле typedef на const_iterator, итд.

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

Я могу себе представить C++ника, который пару десятков лет пишет на плюсах и не знает ни про диграфы, ни про триграфы, ни про перегрузку оператора запятая, ни про volatile, ни про то, как перехватывать исключения в списке инициализаторов в конструкторе… Даже довелось когда-то пообщаться с человеком, который утверждал, что хорошо знает C++, но оказалось, что он не в курсе про placement new…

Но здесь реально странно. На это же натыкаешься каждый раз когда заглядываешь в список методов стандартных контейнеров на cppreference. Тем более, что в C++11 ввели еще и перегрузку вида:

struct A {
  void f() &;
  void f() &&;
  void f() const;
}

если следить за развитием языка, то это то уж не должно было пройти мимо.

Так что остается только повторить: «но черт возьми, как?»

eao197 ★★★★★
()
Ответ на: комментарий от eao197

и не знает ни про диграфы, ни про триграфы, ни про перегрузку оператора запятая, ни про volatile, ни про то, как перехватывать исключения в списке инициализаторов в конструкторе…

Кстати, из этого списка я знаю почти всё, кроме того, как перехватывать исключения в списке инициализаторов в конструкторе. А это может оказаться более актуальным знанием, чем триграфы…

hobbit ★★★★★
() автор топика
Последнее исправление: hobbit (всего исправлений: 1)
Ответ на: комментарий от hobbit

как перехватывать исключения в списке инициализаторов в конструкторе

Я и сам с ходу не смогу такой перехват написать, не помню, чтобы когда-то приходилось это делать, а синтаксис там непривычный. Так что пришлось бы гуглить. Но важно знание о самой такой возможности.

А это может оказаться более актуальным знанием, чем триграфы…

Их, к счастью, уже отменили.

eao197 ★★★★★
()
Ответ на: комментарий от arkhnchul

да они совсем поехавшие там

И очень-очень давно. Это возможно с момента добавления в C++ перегрузки операторов, т.е. где-то с конца 1980-х.

Ну и справедливости ради до совсем недавних пор перегрузка оператора запятая была чуть ли не единственным способом сделать работу с многомерными матрицами похожей на другие языки. Т.е. если очень хотелось писать именно так: a[i,j,k]=v, то приходилось браться за перегрузку запятой. В противном случае нужно было бы делать либо так a[i][j][k]=v, либо так a(i,j,k)=v.

eao197 ★★★★★
()
Ответ на: комментарий от arkhnchul

Поехавшие запиливают перегрузку / для o_o. И применяют как o_o / o_o / o_o. Как тут Внезапно: смайлики в продакшын коде.

Или мержат строки по четыре символа в компайл-тайме. Потому что шаблоны позволяли им на каком-то этапе мутаций плюсов накостылить именно по четыре и не больше.

slackwarrior ★★★★★
()
Ответ на: комментарий от bugfixer

Там понимать нужно, что в set изменение значения по итератору, автоматические инвалидирует все итераторы, что может привести к неприятным ошибкам, поэтому проще запретить и обход set делать через const_iterator.

hatred ★★★
()
Ответ на: комментарий от eao197

хорошо знает C++, но оказалось, что он не в курсе про placement new

считаю, что знание про placement new это уже в категории выше «хорошо»… где-то в районе «хорошо++».

hatred ★★★
()
Ответ на: комментарий от hatred

Там понимать нужно, что в set изменение значения по итератору, автоматические инвалидирует все итераторы

Ну, если совсем «в лоб» - оно конечно ломает set, целиком и полностью. Но существуют use-cases когда ключ не меняется. Или даже ключ меняется, а относительный порядок - нет. И приходится включать «const_cast» базуку, даже на не const set…

bugfixer ★★★★★
()