Std:: in_place_t и друзья на С++ 17
На момент написания статьи cppreference дает достаточно простое определение семейства std::in_place_t
:
struct in_place_t {
explicit in_place_t() = default;
};
inline constexpr std::in_place_t in_place{};
template <class T>
struct in_place_type_t {
explicit in_place_type_t() = default;
};
template <class T>
inline constexpr std::in_place_type_t<T> in_place_type{};
template <size_t I> struct in_place_index_t {
explicit in_place_index_t() = default;
};
template <size_t I>
inline constexpr in_place_index_t<I> in_place_index{};
Однако последний проект стандарта С++ 17 связанный с isocpp.org, имеет более сложное определение (раздел 20.2.7, с. 536):
struct in_place_tag {
in_place_tag() = delete;
};
using in_place_t = in_place_tag(&)(unspecified );
template <class T>
using in_place_type_t = in_place_tag(&)(unspecified <T>);
template <size_t I>
using in_place_index_t = in_place_tag(&)(unspecified <I>);
in_place_tag in_place(unspecified );
template <class T>
in_place_tag in_place(unspecified <T>);
template <size_t I>
in_place_tag in_place(unspecified <I>);
Первая версия проста и понятна, но вторая версия довольно непрозрачна для меня. Итак, вопросы:
-
Какая версия верна, после Issaqua (ноябрь 2016 года)? (Предположительно второй, но возможно, что N4606 еще не обновлен после последней встречи и cppreference имеет.)
-
Очевидно, это изменилось в определенный момент времени; кто-нибудь имеет ссылку на бумагу, в которой упоминается об изменении?
-
Самое главное, может ли кто-нибудь объяснить, как должна работать вторая версия? Как выглядит примерная реализация?
Ответы
Ответ 1
Первая версия является правильной, в настоящее время и, по всей вероятности, будет той, что заканчивается на С++ 17.
Вторая версия была попытка, чтобы разрешить писать in_place
везде, где ничего, с типом или с индексом
std::optional<int> o(std::in_place, 1);
std::any a(std::in_place<int>, 1);
std::variant<int, int> v(std::in_place<0>, 1);
Единственный способ сделать эту работу синтаксиса - сделать in_place
перегруженной функцией, а также потребовать создания псевдонимов in_place*_t
для ссылок на функции. Там нет реальной разницы в реализации, иначе - функции in_place
не должны вызываться, они существуют только для того, чтобы ссылку на них можно было передавать как тег и соответствовать соответствующим типам _t
.
Тем не менее это было слишком умно и вызвало собственные проблемы (например, в отличие от простых типов тегов, они не хорошо реагируют на decay
'd, а plain std::in_place
, являясь перегруженным именем функции, идеальные форвардеры: std::optional<std::optional<int>> o(std::in_place, std::in_place);
не работает, потому что компилятор не может разрешить второй std::in_place
), поэтому он был отброшен в Issaquah, и теперь вам нужно написать
std::optional<int> o(std::in_place, 1);
std::any a(std::in_place_type<int>, 1);
std::variant<int, int> v(std::in_place_index<0>, 1);
Немного менее симпатичный, но более нормальный.