Tristan Brindle Tristan Brindle - 1 day ago 3
C++ Question

std::in_place_t and friends in C++17

As of the time of writing, cppreference gives a reasonably simple definition of the

std::in_place_t
family:

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{};


However, the latest draft of the C++17 standard linked from isocpp.org has a rather more complicated definition (section 20.2.7, page 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>);


The first version is simple and easy to understand, but second version is quite opaque to me. So, questions:


  • Which version is correct, post-Issaqua (November 2016)? (Presumably the second, but it's possible that N4606 hasn't yet been updated after the latest meeting and cppreference has.)

  • Clearly this has changed at some point in time; does anyone have a link to a paper mentioning the change?

  • Most importantly, can anyone explain how the second version is intended to work? What would a sample implementation look like?


Answer

The first version is the right one, currently, and will in all likelihood be the one ending up in C++17.

The second version was an attempt to make it so that you can write in_place everywhere, with nothing, with a type, or with an index:

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

The only way to make this syntax work is to make in_place an overloaded function, and that also requires making in_place*_t aliases for references to functions. There's no real implementation difference otherwise - the in_place functions aren't meant to be called, they exist only so that a reference to them can be passed around as a tag and match the corresponding _t types.

Nonetheless it was too clever and caused its own problems (for instance, unlike plain tag types, they don't respond well to being decay'd), so it got backed out in Issaquah, and now you have to write

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