Что такое точечные объекты настройки и как их использовать? - PullRequest
0 голосов
/ 27 ноября 2018

Последний вариант стандарта c ++ представляет так называемые «объекты точек настройки» ( [customization.point.object] ), которые широко используются библиотекой диапазонов.

Я, кажется, понимаю, что они предоставляют способ написания пользовательских версий begin, swap, data и т. П., Которые обнаруживаются стандартной библиотекой ADL.Это правильно?

Чем это отличается от предыдущей практики, когда пользователь определяет перегрузку, например, begin для своего типа в своем собственном пространстве имен?В частности, почему они объекты ?

Ответы [ 2 ]

0 голосов
/ 28 ноября 2018

«Точка настройки объекта» немного ошибочна.Многие - вероятно, большинство - на самом деле не являются точками настройки.

Такие вещи, как ranges::begin, ranges::end и ranges::swap, являются "настоящими" СРО.Вызов одного из них приводит к некоторому сложному метапрограммированию, чтобы выяснить, существует ли действительный настроенный begin или end или swap для вызова, или если должна использоваться реализация по умолчанию, или если вместо этого должен быть вызовплохо сформирован (в духе SFINAE).Поскольку ряд библиотечных концепций определен в терминах допустимых вызовов CPO (например, Range и Swappable), правильно ограниченный универсальный код должен использовать такие CPO.Конечно, если вы знаете конкретный тип и другой способ получить из него итератор, не стесняйтесь.

Такие вещи, как ranges::cbegin, являются СРО без части "CP".Они всегда делают вещи по умолчанию, так что это не особая настройка.Точно так же объекты адаптера диапазона являются СРО, но в них нет ничего настраиваемого.Классификация их как СРО - это скорее вопрос согласованности (для cbegin) или удобства спецификации (адаптеры).

Наконец, такие вещи, как ranges::all_of, являются квази-СРО или niebloids .Они указываются в качестве шаблонов функций со специальными магическими свойствами блокировки ADL и формулировками ласки, что позволяет использовать их как функциональные объекты.Это в первую очередь предотвращает обнаружение ADL неограниченной перегрузки в пространстве имен std, когда ограниченный алгоритм в std::ranges называется неквалифицированным.Поскольку алгоритм std::ranges принимает пары итератор-страж, он обычно менее специализирован, чем его аналог std, и в результате теряет разрешение перегрузки.

0 голосов
/ 27 ноября 2018

Что такое объекты точек настройки?

Это экземпляры функциональных объектов в пространстве имен std, которые выполняют две цели: first безоговорочно инициируют (заданные) требования к типупо аргументу (ам) затем отправка правильной функции в пространство имен std или через ADL.

В частности, почему они объекты ?

Необходимо обойти вторую фазу поиска, при которой функция ADR напрямую вводится пользователем через ADL (это должно быть отложено по замыслу).Подробнее см. Ниже.

... и как их использовать?

При разработке приложения: вы в основном этого не делаете.Это стандартная функция библиотеки, она добавит проверку концепции к будущим точкам настройки, что, как мы надеемся, приведет к появлению четких сообщений об ошибках, когда вы запутаетесь в экземплярах шаблона.Однако при квалифицированном вызове такой точки настройки вы можете напрямую использовать ее.Вот пример с воображаемым std::customization_point объектом, который придерживается дизайна:

namespace a {
    struct A {};
    // Knows what to do with the argument, but doesn't check type requirements:
    void customization_point(const A&);
}

// Does concept checking, then calls a::customization_point via ADL:
std::customization_point(a::A{});

В настоящее время это невозможно, например, std::swap, std::begin и т. П.

Пояснение(резюме N4381 )

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

  • Их легко ошибиться.Например, обмен объектами в универсальном коде должен выглядеть следующим образом

    template<class T> void f(T& t1, T& t2)
    {
        using std::swap;
        swap(t1, t2);
    }
    

    , но вместо этого сделать квалифицированный вызов std::swap(t1, t2) слишком просто - предоставленный пользователем swap никогда не будет вызываться (см. N4381 , Мотивация и сфера)

  • Более строго, нет способа централизовать (концептуализировать) ограничения на типы, передаваемые таким пользовательским функциям (это такжепочему эта тема приобрела важность с C ++ 20).Снова из N4381 :

    Предположим, что будущая версия std::begin требует, чтобы ее аргумент моделировал концепцию Range.Добавление такого ограничения не повлияет на код, который использует идиоматически std::begin:

    using std::begin;
    begin(a);

    Если вызов метода begin отправляет пользовательскую перегрузкутогда ограничение на std::begin было обойдено.

Решение, описанное в предложении, смягчает обе проблемы с помощью подхода, подобного следующему, воображаемому осуществлению std::begin.

namespace std {
    namespace __detail {
        /* Classical definitions of function templates "begin" for
           raw arrays and ranges... */

        struct __begin_fn {
            /* Call operator template that performs concept checking and
             * invokes begin(arg). This is the heart of the technique.
             * Everyting from above is already in the __detail scope, but
             * ADL is triggered, too. */

        };
    }

    /* Thanks to @cpplearner for pointing out that the global
       function object will be an inline variable: */
    inline constexpr __detail::__begin_fn begin{}; 
}

Во-первых, квалифицированный вызов, например, std::begin(someObject), всегда обходится через std::__detail::__begin_fn, что желательно.Что происходит с неквалифицированным вызовом, я снова ссылаюсь на оригинальную статью:

В случае, если начало называется неквалифицированным после введения std::begin в область действия, ситуация другая.На первом этапе поиска имя begin будет преобразовано в глобальный объект std::begin.Так как поиск нашел объект, а не функцию, вторая фаза поиска не выполняется.Другими словами, если std::begin является объектом, то using std::begin; begin(a); эквивалентен std::begin(a);, который, как мы уже видели, выполняет поиск с учетом аргументов от имени пользователя.

Таким образом, проверка концепции может выполняться в объекте функции в пространстве имен std, за до выполняется вызов ADL для функции, предоставленной пользователем.Обойти это невозможно.

...