Clang以这种方式实现std::nullopt_t
:
struct nullopt_t
{
explicit constexpr nullopt_t(int) noexcept {}
};
constexpr nullopt_t nullopt{0};
为什么不简单:
struct nullopt_t{};
constexpr nullopt_t nullopt{};
答案 0 :(得分:5)
根据cppreference:
std::nullopt_t
必须是LiteralType且不能拥有 默认构造函数。它必须有一个constexpr构造函数 一些实现定义的文字类型。
...如果Clang按照您的建议实施nullopt_t
,那么它就不能满足要求。现在,如果您想知道为什么存在这些要求(一个不同的问题),答案是:
nullopt_t
不支持DefaultConstructible,以支持op = {};
和op = nullopt;
作为解除可选对象的语法。
答案 1 :(得分:5)
正如std::nullopt_t所述:
备注强>
nullopt_t
并非DefaultConstructible
同时支持op = {};
和op = nullopt;
作为解除可选对象的语法。
现在用你的实现测试一下:
struct nullopt_t { };
template <typename T>
struct optional {
optional &operator=(nullopt_t);
optional &operator=(const optional &);
optional &operator=(optional &&);
template <typename U>
optional &operator=(U &&);
};
int main() {
optional<int> oi;
oi = {};
}
此操作失败,因为对operator=
的调用不明确。它可能是尝试调用operator=(nullopt_t)
,也可能是尝试调用operator=(optional &&)
,并且没有语言规则来解决这种歧义。
因此,除非语言规则发生变更,或oi = {};
不再需要有效,否则nullopt_t
或optional
不能默认构建,nullopt_t
1}}是合乎逻辑的选择。
答案 2 :(得分:1)
其他人解释了为什么struct nullopt_t {};
不足。但是至少有两种替代解决方案仍然比目前在libc ++和libstdc ++中使用的解决方案更简单。
(以下是一些测试代码:http://melpon.org/wandbox/permlink/HnFXRjLyqi4s4ikv)
以下是妄想症的增加顺序的替代方案:
struct nullopt_t { constexpr explicit nullopt_t() {} };
constexpr nullopt_t nullopt{};
struct nullopt_t { constexpr explicit nullopt_t(int) {} };
constexpr nullopt_t nullopt{0};
struct nullopt_t {
enum class secret { tag };
explicit constexpr nullopt_t(secret) { }
};
constexpr nullopt_t nullopt{nullopt_t::secret::tag};
struct nullopt_t {
struct secret_tag { explicit secret_tag() = default; };
constexpr explicit nullopt_t(secret_tag, secret_tag) noexcept {}
};
constexpr nullopt_t nullopt{nullopt_t::secret_tag{}, nullopt_t::secret_tag{}};
这些备选方案中的任何一个(即使是备选方案1,据我所知)都足以使赋值o = {}
明确无误。我不知道为什么libc ++和libstdc ++都超越了这个解决方案 - 并注意到libc ++甚至创建了一个两参数构造函数!
如果有人知道libc ++增加妄想症的原因,我很乐意听到它;请在下面创建答案和/或发表评论。