error: base class 'A1' has private copy constructor
Asked Answered
O

2

9

Using Clang 3.7 on windows platform

See following code:

class A1
{
public:
    A1(char* name){}
    virtual ~A1() {}
private:
    A1(const A1&) {}
};

class B1 : public A1
{
public:
    B1(): A1(""){}
};

I get the following error:

 MyFile(31): 8: error: base class 'A1' has private copy constructor
         B1(): A1(""){}
               ^
 MyFile(25): 2: note: declared private here
         A1(const A1&) {}
         ^

Making A1 copy constructor public, eliminates the error!

What happened here?

Note: that by changing (as I should)

A1(const char* name)

I get no errors and all compile as expected

Oestriol answered 23/6, 2016 at 10:4 Comment(5)
@Andrew I think the question is why is the copy constructor required at all.Kamerun
There's no line 31 in your code. Please post a MCVEPorbeagle
A1("") is an error because A1 has no constructor that accepts a string literal; the error you post is probably a cascade from that error (and so should be disregarded as it will disappear once you fix the original error)Porbeagle
@Porbeagle It's weird because clang takes it as a warning by default, not error.Agateware
@Agateware compilers seem to write "warning" for errors in many cases. IMO this causes more confusion than it helps.Porbeagle
G
3

You cannot call the constructor A1(char* name) using a string literal, becase a string literal is not convertible to char* (such deprecated conversion did exist prior to c++11). Or rather, a program that does call the constructor is ill-formed, and the implementation is allowed to refuse to compile.

As such, the overload resolution looks for other alternatives. The only other potential alternative that has the same number of arguments, is the copy constructor.

For whatever reason, clang appears to prefer the implicit conversion from string literal, to A1, thereby creating a temporary, that could be used for copy-initialization, over using the direct construction from the literal. This behaviour leads to the confusing compilation error.

Both alternatives are ill-formed, and clang appropriately warns about it: warning: ISO C++11 does not allow conversion from string literal to 'char *' [-Wwritable-strings]. The program does compile, if you set the standard mode to older than c++11 (in which case the program would be well-formed, even though it does use a deprecated conversion). Interestingly, if we disallow the conversion, then the program compiles even in the current standard mode:

class A1
{
public:
    explicit A1(char* name){} // note the explicit
    virtual ~A1() {}
private:
    A1(const A1&) {}
};

G++ behaves differently and your program compiles fine (with the appropriate warning of course). Both compilers appear to comply to the standard in this regard.

Moral of the story: Always read the warnings as well. In this case, the warning was perfectly clear, and easy to solve, while the same bug indirectly caused an error that was not helpful in the solving of the bug.

Grantham answered 23/6, 2016 at 10:21 Comment(1)
Thanks! When setting copy ctor to public (just for understanding), and running debugger, I see that it runs A1(char* name) ctor first and then copy ctor. when using explicit A1(char* name), only this ctor is called.Oestriol
T
6

I imagine that this is just an artefact of how the diagnostics are generated.

  • First, lookup attempts to find a constructor to match your "call" (it's not a call but whatever)
  • The ctor taking char* doesn't match, as you know
  • The only other candidate is private
  • The compiler tries to see whether it can instantiate a temporary A1 from your "" argument to make that work
  • In doing so, again all it can find is private constructors
  • The compiler decides to complain about that before doing anything else

One might argue that this is a quality of implementation issue.

Amusingly, GCC 6.1.0 (even in pedantic C++14 mode) compiles your code as originally written, spitting out only a warning for the broken literal conversion.

Trombley answered 23/6, 2016 at 10:17 Comment(0)
G
3

You cannot call the constructor A1(char* name) using a string literal, becase a string literal is not convertible to char* (such deprecated conversion did exist prior to c++11). Or rather, a program that does call the constructor is ill-formed, and the implementation is allowed to refuse to compile.

As such, the overload resolution looks for other alternatives. The only other potential alternative that has the same number of arguments, is the copy constructor.

For whatever reason, clang appears to prefer the implicit conversion from string literal, to A1, thereby creating a temporary, that could be used for copy-initialization, over using the direct construction from the literal. This behaviour leads to the confusing compilation error.

Both alternatives are ill-formed, and clang appropriately warns about it: warning: ISO C++11 does not allow conversion from string literal to 'char *' [-Wwritable-strings]. The program does compile, if you set the standard mode to older than c++11 (in which case the program would be well-formed, even though it does use a deprecated conversion). Interestingly, if we disallow the conversion, then the program compiles even in the current standard mode:

class A1
{
public:
    explicit A1(char* name){} // note the explicit
    virtual ~A1() {}
private:
    A1(const A1&) {}
};

G++ behaves differently and your program compiles fine (with the appropriate warning of course). Both compilers appear to comply to the standard in this regard.

Moral of the story: Always read the warnings as well. In this case, the warning was perfectly clear, and easy to solve, while the same bug indirectly caused an error that was not helpful in the solving of the bug.

Grantham answered 23/6, 2016 at 10:21 Comment(1)
Thanks! When setting copy ctor to public (just for understanding), and running debugger, I see that it runs A1(char* name) ctor first and then copy ctor. when using explicit A1(char* name), only this ctor is called.Oestriol

© 2022 - 2024 — McMap. All rights reserved.