为何要为std::default_initializable概念添加冗余的std::constructible_from?
std::default_initializable需要同时包含std::constructible_from<T>和requires { T{}; ::new T; } 你觉得requires { T{}; ::new T; }已经覆盖了所有必要检查,但实际上添加std::constructible_from<T>并非冗余,核心原因如下:
明确对齐标准的核心要求
标准对非数组类型的std::default_initializable有一个核心要求:表达式T()作为未求值操作数必须是良构的(即类型支持T()形式的默认构造)。std::constructible_from<T>直接封装了这一检查,让概念定义更清晰地对应标准措辞,而非依赖隐含行为。适配数组类型的特殊处理
对于数组类型(比如int[5]),T{}(值初始化数组)和::new T(动态分配并默认初始化数组)都是合法的,但std::constructible_from<T>会返回false(因为数组不能通过T()语法构造)。不过标准中数组类型的default_initializable是通过概念偏特化处理的:只要数组元素类型满足default_initializable,数组类型就满足。主模板中的constructible_from<T>会将数组类型排除在主逻辑之外,交给偏特化处理,这完全符合标准要求。避免边缘场景的歧义
虽然大部分非数组类型中,T{}和::new T的合法性会隐含T()的合法性,但存在罕见场景:某些类型可能允许T{}和::new T,但T()语法不合法。这类类型通常也不满足default_initializable的核心要求,添加constructible_from<T>可以明确排除它们,避免依赖编译器的隐含行为。提升定义的可读性
将constructible_from<T>作为单独条件,能直观表达“类型必须支持默认构造”的核心语义,而requires子句则补充检查值初始化、动态分配场景下的默认初始化合法性,让概念的逻辑拆分更清晰,便于理解。
内容的提问来源于stack exchange,提问作者xmllmx

