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);

Немного менее симпатичный, но более нормальный.