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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With