Что такое объекты точек настройки?
Это экземпляры функциональных объектов в пространстве имен 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 для функции, предоставленной пользователем.Обойти это невозможно.