std::in_place_t 和 C++17 中的朋友们
std::in_place_t and friends in C++17
截至撰写本文时,cppreference 给出 std::in_place_t
家族的 reasonably simple definition:
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{};
然而,C++17 标准的最新草案 linked from 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>);
第一个版本简单易懂,第二个版本对我来说比较晦涩。所以,问题:
哪个版本是正确的,post-Issaqua(2016 年 11 月)? (大概是第二个,但有可能是N4606在最近的会议之后还没有更新,而cppreference已经更新了。)
很明显,这在某个时间点发生了变化;有没有人有 link 一篇提到更改的论文?
最重要的是,谁能解释一下第二个版本是如何工作的?示例实现是什么样的?
第一个版本是当前正确的版本,并且很可能是 C++17 中的版本。
第二个版本是 an attempt 允许一个人在任何地方写 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
的反应不佳,而普通的 std::in_place
,是重载的函数名称,完美转发器的行为不当:std::optional<std::optional<int>> o(std::in_place, std::in_place);
不起作用,因为编译器无法解析第二个 std::in_place
),因此在 Issaquah 中 it got backed out,现在你必须写
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);
不那么漂亮,但更理智。
截至撰写本文时,cppreference 给出 std::in_place_t
家族的 reasonably simple definition:
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{};
然而,C++17 标准的最新草案 linked from 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>);
第一个版本简单易懂,第二个版本对我来说比较晦涩。所以,问题:
哪个版本是正确的,post-Issaqua(2016 年 11 月)? (大概是第二个,但有可能是N4606在最近的会议之后还没有更新,而cppreference已经更新了。)
很明显,这在某个时间点发生了变化;有没有人有 link 一篇提到更改的论文?
最重要的是,谁能解释一下第二个版本是如何工作的?示例实现是什么样的?
第一个版本是当前正确的版本,并且很可能是 C++17 中的版本。
第二个版本是 an attempt 允许一个人在任何地方写 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
的反应不佳,而普通的 std::in_place
,是重载的函数名称,完美转发器的行为不当:std::optional<std::optional<int>> o(std::in_place, std::in_place);
不起作用,因为编译器无法解析第二个 std::in_place
),因此在 Issaquah 中 it got backed out,现在你必须写
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);
不那么漂亮,但更理智。