Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 512
Tristan Brindle Avatar asked Dec 02 '16 01:12

Tristan Brindle


1 Answers

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 allow one to 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, and plain std::in_place, being an overloaded function name, misbehaves with perfect forwarders: std::optional<std::optional<int>> o(std::in_place, std::in_place); doesn't work because the compiler can't resolve the second std::in_place), 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);

A little less pretty, but more sane.

like image 126
T.C. Avatar answered Nov 19 '22 08:11

T.C.