C++17及以上explicit默认构造函数类型的{}隐式转换序列问题
适用的标准条文说明
针对示例场景,以及LWG 3562工作组给出的结论,核心适用的标准规则如下:
- 复制列表初始化对
explicit构造函数的限制
赋值运算符的参数传递属于隐式转换场景,本质是对参数的复制初始化。根据列表初始化规则,当使用空初始化列表{}对非聚合类类型进行复制初始化时,若类的默认构造函数带有explicit修饰,则该初始化非法:explicit构造函数仅能用于直接初始化,不能用于隐式转换场景。 - 隐式转换序列的列表初始化规则
当重载决议判断初始化列表能否转换为函数参数类型时,若目标类型为非聚合类类型,将按照复制列表初始化的规则构造转换序列,此时explicit默认构造函数不会被纳入可行的隐式转换路径。 - 复制列表初始化补充约束
复制列表初始化场景下,即使重载决议过程中临时选中了explicit构造函数,最终也会判定该初始化病构,进一步确认了explicit默认构造的类型无法通过{}完成隐式转换。
以上规则恰好对应LWG 3562工作组提到的「当前标准措辞禁止从{}生成到explicit默认构造类型的隐式转换序列」的结论,完全适用于本次示例。
编译器行为正确性判定
示例程序是良构的,Clang的接受行为符合标准要求,GCC的报错属于实现缺陷:
- 对于#3处的赋值语句,重载候选#2的参数类型为
my_nullopt_t,而{}无法隐式转换为该类型(其默认构造函数为explicit),因此#2从一开始就不属于可行重载。 - 仅重载候选#1是可行的,不存在歧义,赋值语句会调用拷贝赋值运算符,用
{}默认构造的S临时对象完成赋值。
GCC错误地将explicit默认构造函数纳入了隐式转换的可行路径,导致误判两个重载都可行,触发歧义报错。
内容的提问来源于stack exchange,提问作者dfrib
相关产品推荐
相关产品推荐

