Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access to own private constructors via derived class constructor inheritance

Consider:

struct B {
    void f();
private:
    B(int, int = 0);
};
struct D : B { using B::B; };
void B::f() {
    auto a = D{0};
    auto b = D(0);
    auto c = D(0, 0);
    D x{0};
    D y(0);
    D z(0, 0);
}

GCC accepts (since 7.1; previously rejects all). Clang accepts b and xyz but rejects a and c. MSVC rejects all in C++14 mode but accepts all in C++17 mode.

Which compiler(s) are correct? Did the rules change between C++14 and C++17 - if so, why in C++14 is B::f not allowed to access its own constructors (named via D)? And why does Clang only accept the (function-style) cast at b and not list-initialization at a or constructor call at c?

(Making B a friend of D makes Clang accept, except for older versions (3.8 and older), and MSVC in C++14 mode. It doesn't make a difference to gcc.)

Now, I know that C++14 says:

A constructor so declared [as an inheriting constructor] has the same access as the corresponding constructor in [the base class] X.

And in C++17 the rules were simplified, clarified and moved to [namespace.udecl]:

A using-declarator that names a constructor does not create a synonym; instead, the additional constructors are accessible if they would be accessible when used to construct an object of the corresponding base class, and the accessibility of the using-declaration is ignored.

So, I thought that perhaps "has the same access as" means the inherited constructors are private to D so only accessible to D (and its friends), rather than private to B (and so accessible to B::f) as are their corresponding constructors in B. But under that interpretation, one would be able to subvert access checking and access private constructors of one's base class by inheriting them. So surely in C++14 the intent of the wording is the same as expressed more clearly in C++17 - but then why does MSVC change its behavior depending on language version?

like image 794
ecatmur Avatar asked Dec 16 '20 09:12

ecatmur


People also ask

Can private be accessed by derived class?

Private members of the base class cannot be used by the derived class unless friend declarations within the base class explicitly grant access to them.

How do you access private members in a derived class?

You can directly access the private member via its address in memory. If you're comfortable with it that is. You could have a function in the base class that returns the address of the private member and then use some wrapping function in the derived class to retrieve, dereference and set the private member.

Can a constructor be inherited by derived class?

In inheritance, the derived class inherits all the members(fields, methods) of the base class, but derived class cannot inherit the constructor of the base class because constructors are not the members of the class.

Can private constructor class inherited in Java?

Java doesn't prevent sub-classing of class with private constructors. What it prevents is sub-classes which cannot access any constructors of its super class. This means a private constructor cannot be used in another class file, and a package local constructor cannot be used in another package.


1 Answers

There were a lot of issues with the original specification of inheriting constructors (as they were then called), several of which involved access control. They were fixed retroactively, which is supposed to be official for C++14 only (as the most recently published standard at the time). Many compilers choose to apply such changes to even earlier versions where it makes sense (i.e., C++11).

However, it wasn’t possible to access private base-class constructors even in C++14 as published, because the implicit definition of an inheriting private constructor would fail to invoke the actual private constructor in the base. MSVC is therefore correct except for not applying the defect report to C++14; Clang appears to have a separate bug in handling certain kinds of initialization; perhaps it accepts D(0) because it’s defined in terms of D …(0);. Current GCC is correct in all cases.

like image 193
Davis Herring Avatar answered Sep 23 '22 01:09

Davis Herring