为什么编译器总是选择非显式构造函数进行复制列表初始化?

Why compiler always choose non explicit constructor for copy-list-initialization?

提问人:Kishan Parmar 提问时间:8/8/2023 最后编辑:dfribKishan Parmar 更新时间:8/9/2023 访问量:114

问:

对于下面的代码,编译器会选择非显式构造函数吗?

struct S {
    S() = default;
    explicit S(S & cp) {
      std::cout << "explicit" << std::endl;
    } 
    S(const S & cp) {
      std::cout << "copy" << std::endl;
    };
};


int main() {  
    S s1; 
    S s2 = {s1}; 
}

https://en.cppreference.com/w/cpp/language/list_initialization 年开始,它说 copy-list-ini...显式和非显式构造函数都考虑在内,但只能调用非显式构造函数

https://godbolt.org/z/YqWWs9jqY

C++ C++11 语言律师 复制构造函数

评论

1赞 BoP 8/8/2023
一个类有两个复制构造函数是极其不寻常的。复制构造函数更不寻常。如果你是初学者,就不要掉进这个兔子洞!它永远不会在实际代码中发生。explicit
0赞 Kishan Parmar 8/8/2023
这是在实际代码中,并且混淆了为什么编译器选择副本而不是显式。
0赞 dfrib 8/9/2023
应选择显式构造函数,否则会导致程序格式不正确,尤其是从 CWG 2137 开始。然而,Clang 尚未实施 CWG 2137,由于措辞的变化,GCC 可能没有考虑到对显式构造函数的影响。

答:

7赞 dfrib 8/8/2023 #1

程序格式不正确,但存在实现分歧

CWG 2137开始,您的程序应该格式不正确。然而,这里存在实现分歧,其中 Clang 尚未实现 CWG 2137,而 GCC 已经实现了 CWG 2137 的大部分内容,但可能在上下文 copy-list-initialization(具有同一类的单个元素)中缺少显式构造函数的情况。

错误报告(我刚刚发布了 GCC 报告):

规则

[dcl.init.list] 控制列表初始化的规则,以及以下内容:

S s2 = {s1}; 

自CWG 2137起,[dcl.init.list]/3.7适用:

否则,如果 T 是类类型,则考虑构造函数。枚举适用的构造函数,并通过重载解析([over.match]、[over.match.list])选择最佳构造函数。如果需要缩小转换(见下文)来转换任何参数,则程序格式不正确。

由于没有初始值设定项列表构造函数,因此 [over.match.list]/1.2 适用:

/1.2 否则,或者如果未找到可行的初始值设定项列表构造函数,则再次执行重载解析,其中候选函数是类 T 的所有构造函数,参数列表由初始值设定项列表的元素组成。

在 copy-list-initialization 中,如果选择了显式构造函数,则初始化格式不正确。

由于显式构造函数是更好的匹配,因此应该选择它,之后程序格式不正确,因为 [over.match.list]/1 不允许在 copy-list-initialization 中使用显式构造函数。

GCC 和 Clang 接受该计划都是错误的,但 Clang 由于尚未实施 CWG 2137,而 GCC 则由于缺少 CWG 2137 引入的一些边缘情况而不太适用。

评论

0赞 Kishan Parmar 8/11/2023
这是编译器的测试用例之一,它应该选择正确的编译器。