10

I am trying to test protected methods and constructors of my class. For this purpose, I tried to subclass it, and re-export its members as public with C++11 using keyword:

class Foo {
  protected:
   Foo(int i) {}
   void run() {}
};

class TestableFoo : public Foo {
  public:
   using Foo::Foo;
   using Foo::run;
};

int main() {
  TestableFoo foo(7);
  foo.run();
}

However, both g++ and clang++ fail to compile it, producing the following error:

test.cpp:13:15: error: ‘TestableFoo::TestableFoo(int)’ is protected
    using Foo::Foo;
               ^
test.cpp:18:16: error: within this context
   TestableFoo foo(7);
                    ^

TestableFoo constructor is still protected, even though run method becomes public (I confirmed it separately). Why is that so? I could understand either decision (inheriting vs. overwriting visibility), but why is there an inconsistency between methods and constructors?

rburny
  • 1,559
  • 1
  • 9
  • 28
  • Related - http://stackoverflow.com/questions/24014240/c11-declaring-factory-a-friend-of-base-class - but doesn't answer *why* – Praetorian Jul 08 '14 at 15:40
  • 2
    The "why" is probably because otherwise it would change the accessibility of *all* the constructors, which might be considered a worse evil than the inconvenience of not being able to inherit them in this situation. But that's just a guess. – Mike Seymour Jul 08 '14 at 15:49

2 Answers2

5

The Standard explicitly states that inherited constructors retain their access levels:

12.9 Inheriting constructors [class.inhctor]

1 A using-declaration (7.3.3) that names a constructor implicitly declares a set of inheriting constructors. The candidate set of inherited constructors from the class X named in the using-declaration consists of actual constructors and notional constructors that result from the transformation of defaulted parameters as follows:

[list of cases omitted]

4 A constructor so declared has the same access as the corresponding constructor in X. It is deleted if the corresponding constructor in X is deleted (8.4).

You can call it directly of course:

TestableFoo(int i) : Foo(i) { }
TemplateRex
  • 69,038
  • 19
  • 164
  • 304
1

This behaviour is conform with what the standard says (ISO/IEC 14822:2011 12.9, §4) :

A constructor so declared has the same access as the corresponding constructor in X.

where X is the base class from which the constructors are inherited.

To get the desired behaviour, you can use :

class TestableFoo : public Foo {
  public :
    TestableFoo(int i) : Foo(i) { }
    using Foo::run;
};
Sander De Dycker
  • 16,053
  • 1
  • 35
  • 40