编译器提供私有构造函数作为程序代码中的候选者

D. *_*nes 3 c++ gcc constructor clang

#include <iostream>

using namespace std;

class Test{
private:
    Test(int a, int b=0)
    {
        cout << "private constructor\n";
    }
public:
    Test(int a)
    {
        cout << "public constructor\n";
    }
};

int main()
{
    Test t(1);
}
Run Code Online (Sandbox Code Playgroud)

当我试图编译代码时gcc说:

test.cpp: In function ‘int main()’:
test.cpp:20:10: error: call of overloaded ‘Test(int)’ is ambiguous
  Test t(1);
          ^
test.cpp:12:2: note: candidate: Test::Test(int)
  Test(int a)
  ^
test.cpp:7:2: note: candidate: Test::Test(int, int)
  Test(int a, int b=0)
  ^
test.cpp:5:7: note: candidate: Test::Test(const Test&)
 class Test{
       ^
Run Code Online (Sandbox Code Playgroud)

clang说:

test.cpp:20:7: error: call to constructor of 'Test' is ambiguous
        Test t(1);
             ^ ~
test.cpp:7:2: note: candidate constructor
        Test(int a, int b=0)
        ^
test.cpp:12:2: note: candidate constructor
        Test(int a)
        ^
test.cpp:5:7: note: candidate is the implicit copy constructor
class Test{
      ^
1 error generated.
Run Code Online (Sandbox Code Playgroud)

模棱两可的原因是什么?既然Test(int,int)是私人的,就不应该把它叫进来Test t(1).一个可能的答案是(我最初的想法),它使构造函数的两个相同的签名成为可能,即Test()可以int在私有构造函数中只调用一个.但是在程序代码Test t(1)中只对公共构造函数可行,所以它不应该提供私有构造函数作为候选者.为什么这么说?

Sto*_*ica 5

根据标准,明确的原因是模棱两可的:

[class.access/5]

应该注意的是,它是对受控制的成员和基类的访问,而不是它们的可见性.成员的名称仍然可见,并且当这些成员和基类不可访问时,仍会考虑对基类的隐式转换.建立对给定构造的解释而不考虑访问控制.如果建立的解释使用了不可访问的成员名称或基类,则该构造是不正确的.

这种可见性意味着编译器必须考虑构造的两个重载,并且它们对于您传递的参数同样有用.

由于构造函数是私有的,因此只能使用类及其成员的范围进行访问,因此只需删除默认参数值即可.您可以通过其他方式在类定义中维护此默认值.例如,通过在类实现文件中引入一个常量:

int const default_b = 0;

// ...

return Test{a, default_b};
Run Code Online (Sandbox Code Playgroud)