4
#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);
}

When I attempted to compile the code gcc says:

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{
       ^

and clang says:

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.

What is the reason of the ambiguity? Since Test(int,int) is private, it shouldn't be possible to call it in Test t(1). A possible answer is (what I thought initially), it makes two identical signatures of constructors possible, i.e Test() can be called with only one int in private constructor. But in the program code Test t(1) is only feasible for public constructors, so it shouldn't offer the private constructor as a candidate. Why does it say that?

D. Jones
  • 461
  • 2
  • 16
  • 1
    same reason as this: http://stackoverflow.com/questions/39042240/why-is-a-public-const-method-not-called-when-the-non-const-one-is-private/39042574#39042574 – NathanOliver May 05 '17 at 21:54

1 Answers1

5

It's ambiguous for an explicit reason, as according to the standard:

[class.access/5]

It should be noted that it is access to members and base classes that is controlled, not their visibility. Names of members are still visible, and implicit conversions to base classes are still considered, when those members and base classes are inaccessible. The interpretation of a given construct is established without regard to access control. If the interpretation established makes use of inaccessible member names or base classes, the construct is ill-formed.

And that visibility means that the compiler must consider both overloads for the construction, and they are equally good for the argument you passed.

Since the constructor is private, and therefore only accessible withing the scope of the class and its members, just remove the default parameter value. You can maintain this default value in your classes definition in other ways. For instance, by introducing a constant in the classes implementation file:

int const default_b = 0;

// ...

return Test{a, default_b};
StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • A one liner refinement to the answer: name resolution happens before access privileges are checked. +1. – R Sahu May 05 '17 at 22:29