Подтвердить что ты не робот

Что делает оператор. * &?

Я наткнулся на этот вопрос, у которого был ответ, который использовал нечетную конструкцию:

typedef std::queue<int> Q;
typedef Q::container_type C;

C & get (Q &q)
{
    struct hack : private Q {
        static C & get (Q &q) {
            return q.*&hack::c;
        }
    };
    return hack::get(q);
}

Я обычно следую за тем, что q имеет доступ к своему собственному элементу c, на который ссылается функция get. Но я затрудняюсь это объяснить. Что происходит именно с .*&, и почему это разрешено?

4b9b3361

Ответ 1

typedef std::queue<int> Q;

Q является адаптированным контейнером queue.

typedef Q::container_type C;

C является базовым контейнером Q, который является deque<int>.

C & get (Q &q) {

get принимает a queue и возвращает a deque. Фактически он возвращает deque, который обертывает queue: обычными способами это невозможно.

  struct hack : private Q {

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

Нет hack когда-либо создаётся.

    static C & get (Q &q) {

hack::get имеет ту же подпись, что и сама get. Фактически мы делегируем всю работу get этому методу.

      return q.*&hack::c;

эта строка должна быть разбита. Я сделаю это в следующих строках:

      using mem_ptr_t = C Q::*; // aka typedef C Q::*mem_ptr_t;
      mem_ptr_t c_mem_ptr = &hack::c;
      C& ret = q.*c_mem_ptr;
      return ret;

Первая строка определяет тип указателя-члена для поля типа C в пределах Q. Оба способа именования этого типа С++ 11 и С++ 03 являются уродливыми.

Вторая строка получает указатель на поле C в Q. Он делает это через отверстие в системе типов С++. &hack::c логически относится к типу C hack::* - указателю на элемент типа C в классе типа hack. Фактически, поэтому мы можем получить к нему доступ в static член hack. Но вопрос C на самом деле находится в Q, поэтому фактический тип выражения в С++ - это C Q::*: указатель на переменную-член Q.

Вы не можете напрямую получить этот указатель-член внутри hack - &Q::c является незаконным, но &hack::c не является.

Вы можете думать о указателях-членах как "набранные смещения" на другой тип: &hack::c - это "смещение" C внутри Q, а также знание того, что он имеет тип C. Теперь это не так - это непрозрачное значение, которое сообщает компилятору, как получить C от Q, но это помогает думать об этом таким образом (и это может быть реализовано таким образом в простых случаях).

Затем мы используем этот указатель-член вместе с Q&, чтобы получить C из Q. Получение указателя участника ограничено защитой: использование его не является! Способ, которым мы это выполняем, - это оператор .*, который является оператором разыменования членов, который вы можете передать либо указателями на функции-членами, либо членами справа, и экземплярами класса слева.

instance .* member_ptr - выражение, которое находит элемент, "указываемый" на member_ptr в пределах instance. В исходном коде все было сделано в одной строке:

instance .* &class_name::member_name

поэтому он выглядел как оператор .*&.

    }
  };

а затем мы закрываем статический метод и класс hack и:

  return hack::get(q);
}

назовите его. Этот метод дает доступ к protected состоянию: без него, protected члены могут быть доступны только в дочерних классах одного и того же экземпляра. Используя это, мы можем получить доступ к protected членам любого экземпляра, не нарушая ни одного бита стандарта.

Ответ 2

Это взломать, как указывает номенклатура.

.* берет объект с левой стороны и указатель члена справа, и разрешает элемент с указанным элементом данного объекта. & является, конечно, ссылочным оператором; &Class::Member возвращает указатель на элемент, который сам по себе не может быть разыменован, но который может использоваться с операторами .* и ->* (последний из них самый худший из всех операторов С++). Таким образом, obj .* &Class::Member имеет тот же эффект, что и obj.Member.

Причина использования этой более сложной версии сводится к лазейке в семантике защиты; в основном, он позволяет получить доступ к элементам protected объекта базового класса, даже если объект не имеет тот же тип, что и класс, выполняющий этот грязный взлом.

Лично я думаю, что трюк слишком умный наполовину. Обычно я пишу такой код:

struct hack : private Q {
    static C & get (Q &q) {
        return static_cast<hack &>(q).c;
    }
};

Что технически немного менее безопасно, но не скрывает, что происходит.

. * Ну, обычно я бы вообще ничего не писал. Но я буквально сделал это раньше сегодня, поэтому я не могу бросить камни.