C++17:指定函数默认实参时是否强制拷贝省略?能否规避抛出构造?
我希望避免调用std::source_location的拷贝构造函数(因其并非noexcept),因此编写了如下代码:
auto foo( std::source_location loc = std::move(std::source_location::current()) ) noexcept -> void { /* ... */ }
由于std::source_location::current()方法和移动构造函数均为noexcept,我将foo函数也声明为noexcept。但我想知道:C++标准是否有明确保证,在此场景下编译器不会使用拷贝构造函数(例如因赋值操作导致)或其他可能抛出异常的构造函数/运算符?
回答
C++标准明确保证该场景下不会调用拷贝构造函数,核心依据如下:
默认参数的初始化逻辑:当省略
foo的实参时,默认表达式std::move(std::source_location::current())会生成一个std::source_location类型的右值。根据C++重载决议规则,右值初始化对象时会优先匹配移动构造函数——而std::source_location的移动构造函数是被标准要求为noexcept且可访问的,因此编译器必然选择移动构造来初始化参数loc。无强制拷贝替换规则:C++标准中没有任何条款会要求编译器将此处的移动操作替换为拷贝操作。只要移动构造函数存在且满足重载决议的匹配条件,编译器就不能擅自改用拷贝构造。
noexcept声明的合理性:你将
foo标记为noexcept是完全安全的——默认参数的初始化流程(current()调用+移动构造)均为noexcept,只要函数体内没有抛出异常的操作,整个函数的noexcept声明符合标准要求,不会触发未定义行为。
需要补充的是:如果主动向foo传递一个std::source_location的左值,此时会触发拷贝构造函数,但这属于显式传递左值的场景,不在你当前默认参数的讨论范围内。
内容的提问来源于stack exchange,提问作者acinis

