12

The following code doesn't compile:

class C
{
    private:

        int m_x;

    protected:

        C(int t_x) : m_x(t_x) { }

};

class D : public C
{
    public:

        using C::C;

};

int main(int argc, char **argv)
{
  D o(0);
}

The compiler's objection is that the constructor for C is declared protected, meaning that I can't access it from main. In other words, it seems like the using declaration drags the original visibility of the identifier with it, despite the fact that it lives in the public block.

Two questions:

  1. Why does this happen? (Both in terms of the rules for how this works, and the rationale for making those rules).
  2. Is there any way I can get around this without explicitly writing a constructor for D?
Daniel McLaury
  • 4,047
  • 1
  • 15
  • 37
  • 1
    [This](https://stackoverflow.com/questions/55927739/using-statement-and-protected-constructor) answers question 1. Not sure if there is a general solution for question 2. – NathanOliver Jun 26 '19 at 16:21
  • Why do you want to make the constructor protected? If it is to prevent construction of `C` objects then make the destructor for C pure virtual, which it should be anyway. –  Jun 26 '19 at 16:21
  • @NeilButterworth: Well, if nothing else it seems that *semantically* the accessibility of a member should tell you where it can be called from. Also, I've heard that introducing the first virtual member to a class incurs some costs and should be avoided where possible, although that may be out of date nowadays. – Daniel McLaury Jun 26 '19 at 16:34
  • If you have derivation, you almost always need a base class destructor - most often, this will be pure virtual. –  Jun 26 '19 at 16:59

2 Answers2

13

This is a subtle one. In C++, employing the using keyword on a base class constructor is called inheriting constructors and works differently than what a using keyword typically does. Specifically, note that

If overload resolution selects an inherited constructor, it is accessible if it would be accessible when used to construct an object of the corresponding base class: the accessibility of the using-declaration that introduced it is ignored.

(Emphasis mine. Source)

In other words, the fact that you've included the using declaration in a public section doesn't actually make those constructors public.

I believe that, in this case, you may have to define your own constructors to match the base type constructors.

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • 1
    Do you have any idea what the rationale for this is? – Daniel McLaury Jun 26 '19 at 16:26
  • 2
    Sorry, no clue. This is news to me! – templatetypedef Jun 26 '19 at 16:26
  • 4
    I think rationale is based on that `C` can have many overloaded constructors with different accessibility. Since with `using C::C;` you can't select only some of constructors this would level out accessibility of all constructors. This doesn't sound like a good idea, most people will expect unchanged accessibility of inheriting constructors. – Marek R Jun 26 '19 at 16:43
0

statement using C::C; is not about increasing visibility, but, about since c++11 feature called inheriting constructors, making constructor of base class C invoking when derived D is constructed. You cant change access modifier of constructor that way. But, you can change access modifier, of any function by explicitly redeclaring it in a derived class with the different access modifier.

Boki
  • 637
  • 3
  • 12
  • What do you mean by "explicitly redeclaring it in a derived class with the different access modifier"? – Daniel McLaury Jun 26 '19 at 16:44
  • @DanielMcLaury simply, for example if you have protected function in base class with signature int func(int), you can redeclare it in derived class as public and do whatever you want in it(among others invoke base protected func), that way you basically change access modifier of member function in base class. Same is true if function is virtual in base etc. – Boki Jun 26 '19 at 16:58
  • Are you saying I can redeclare it without redefining it? If so, how? – Daniel McLaury Jun 26 '19 at 17:00
  • @DanielMcLaury, you need to define it, as well, of course. But from new definition you can call base implementation, and that way change acces modifier. – Boki Jun 26 '19 at 17:02
  • @DanielMcLaury also, if you want to use inherited implementation, you can use using directive in public section of derived class and that way change modifier w/o redefining it. Put in public section simply using C::func. And that feature is not applicable for constructors. – Boki Jun 26 '19 at 17:15