Читать «Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14» онлайн - страница 37

Скотт Мейерс

Но как распознать, когда используется прокси-объект? Программное обеспечение, использующее невидимый прокси, вряд ли станет его рекламировать. Ведь эти прокси-объекты должны быть невидимыми, по крайней мере концептуально! И если вы обнаружите их, то действительно ли следует отказываться от auto и массы преимуществ, продемонстрированных для него в разделе 2.1?

Давайте сначала зададимся вопросом, как найти прокси. Хотя “невидимые” прокси-классы спроектированы таким образом, чтобы при повседневном применении “летать вне досягаемости радара программиста”, использующие их библиотеки часто документируют такое применение. Чем лучше вы знакомы с основными проектными решениями используемых вами библиотек, тем менее вероятно, что вы пропустите такой прокси незамеченным.

Там, где документация слишком краткая, на помощь могут прийти заголовочные файлы. Возможность сокрытия прокси-объектов в исходном коде достаточно редка. Обычно прокси-объекты возвращаются из функций, которые вызываются клиентами, так что сигнатуры этих функций отражают существование прокси-объектов. Например, вот как выглядит std::vector<bool>::operator[]:

namespace std { // Из стандарта С++

template <class Allocator>

class vector<bool, Allocator> {

public:

 …

 class reference { … };

 reference operator[](size_type n);

};

}

В предположении, что вы знаете, что operator[] у std::vector<T> обычно возвращает T&, необычный возвращаемый тип у operator[] в данном случае должен навести вас на мысль о применении здесь прокси-класса. Уделяя повышенное внимание используемым интерфейсам, часто можно выявить наличие прокси-классов.

На практике многие разработчики обнаруживают применение прокси-классов только тогда, когда пытаются отследить источник таинственных проблем при компиляции или отладить никак не проходящий тесты модуль. Независимо от того, как вы его обнаружили, после того как выясняется, что auto определен как выведенный тип прокси-класса вместо “проксифицируемого” типа, решение не требует отказа от auto. Само по себе ключевое слово auto проблемой не является. Проблема в том, что auto выводит не тот тип, который вам нужен. Решение заключается в том, чтобы обеспечить вывод другого типа. Способ достижения этого заключается в том, что я называю идиомой явной типизации инициализатора.

Идиома явной типизации инициализатора включает объявление переменной с использованием auto, но с приведением инициализирующего выражения к тому типу, который должен вывести auto. Например, вот как можно использовать эту идиому, чтобы заставить highPriority стать переменной типа bool:

auto highPriority = static_cast<bool>(features(w)[5]);

Здесь features(w)[5] продолжает, как и ранее, возвращать объект типа std::vector<bool>::reference, но приведение изменяет тип выражения на bool, который auto затем выводит в качестве типа переменной highPriority. Во время выполнения программы объект std::vector<bool>::reference, который возвращается вызовом std::vector<bool>::operator[], преобразуется в значение bool и в качестве части преобразования выполняется разыменование все еще корректного указателя на std::vector<bool>, возвращенного вызовом features. Это позволяет избежать неопределенного поведения, с которым мы сталкивались ранее. Затем к битам, на которые указывает указатель, применяется индексация с индексом 5 и полученное значение типа bool используется для инициализации переменной highPriority.