Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

error: base class 'A1' has private copy constructor

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

like image 304
Mr. G Avatar asked Jun 23 '16 10:06

Mr. G


2 Answers

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.

like image 171
Lightness Races in Orbit Avatar answered Sep 20 '22 15:09

Lightness Races in Orbit


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.

like image 20
eerorika Avatar answered Sep 19 '22 15:09

eerorika