0

I have problems understanding the correct behavior of inheriting constructors from a base class. In my particular case, I have a crtp-base class with private constructors in order to prevent an instantiation of the base class (as abstract class). Now, the crtp-base befriends the derived class and the derived class inherits the base class constructors with a using statement. This works well for the default, copy and move constructor but fails for custom constructors. Is there a way to achieve this without reimplenting all constructors in the derived class?

#include <iostream>

template <typename d_t>
class base
{
    friend d_t;

    base()
    {
        std::cout << "base: ctor()\n";
    }
    base(base const & other) = default;
    base(base && other) = default;

    base(int)
    {
        std::cout << "base: ctor()\n";
    }
};

class derived: public base<derived>
{
public:

   using base<derived>::base;
};

int main()
{
   derived d{};
   derived d1{d};
   derived d2{std::move(d1)};
   // derived d3{1};  //does not compile!
}

EDIT

AFAIK cppreference says that accessibility of constructors is not changed by the access specifier of the using declaration in the derived class, which is different to other member functions. But I have seen this kind of code compiling and running and I am not sure if I understood the using-declaration correctly. Of course I'll investigate the other code further to see what is going on but I wanted to know if there is something hidden that I might miss.

  • `friend d_t;` that looks wrong? – Yakk - Adam Nevraumont Nov 23 '18 at 00:39
  • @Yakk-AdamNevraumont Why? – songyuanyao Nov 23 '18 at 00:50
  • `_t` is reserved in POSIX? – user4581301 Nov 23 '18 at 01:07
  • 1
    Making the derived class a friend does not make much sense as you would get almost the same effect by make everything protected instead of private. The main difference would be if you derive other classes from your derived classes but given that you are using the CRTP pattern, it is probably not the case. Thus in the end, I think you are using the wrong tool for the job. – Phil1970 Nov 23 '18 at 01:12
  • @Phil1970 true. I coluld make it protected. And if I understand https://en.cppreference.com/w/cpp/language/using_declaration correct then it should not even work. It is just that I've seen this kind of code compiling and I am completely out of explanation why? So I was wondering if there is a bit that I am not aware of which makes this work. – guest3450132 Nov 23 '18 at 09:03
  • @Phil1970, sorry but it is wrong to make a CRTP protected. This coudl lead to misuse of the base class causing UB. To prevent such a use case you have to make the constructor of the CRTP base private and befriend the derived class, such that only the derived class can call the base class constructors. – guest3450132 Nov 30 '18 at 09:22
  • I am not sure how a protected constructor can cause misuse. However, with friendship, a misuse is possible by having members of type `base` in `derived` class at arbitrary offset and not deriving from `base`. – Phil1970 Dec 02 '18 at 01:36
  • With misuse I meant that I could derive from the crtp-base but instantiate it with a different class (see https://godbolt.org/z/ILKr8e, source#1). While with the private-friend idiom I prevent such misuse since my derived class could not be created anymore (https://godbolt.org/z/ILKr8e source#2). However, I just figured out that there seems to be a bug with gcc's concept implementation that causes access modifiers to be ignored. – guest3450132 Dec 03 '18 at 12:41

1 Answers1

0

The short answer is yes, just make them public in the base class. Inherited constructors have the same accessibility in the derived class as they do in the base. There's no way to make a private base class constructor public in the derived class.

But abstract base classes can't be instantiated on their own anyway. There's no need to make their constructors private.

CRTP has no bearing on any of the above. If you only needed it for the friend declaration, then you can do without the pattern.

class base
{
public:
  base()
  {    
  }

  base(int)
  {
    std::cout << "base: ctor()\n";
  }

  virtual void foo() = 0;
};

class derived: public base
{
public:
  using base::base;

  void foo() override
  {
    // details...
  }
};

int main()
{
  derived d{};
  derived d1{d};
  derived d2{std::move(d1)};
  derived d3{1};  //Works!!
}

Further reading on cppreference: Using-declaration

Peter Ruderman
  • 12,241
  • 1
  • 36
  • 58
  • 1
    Thanks for the reply. I know that this solves the problem, but going to virtual polymorphism is not an option. The code is completely based on static polymorphism. – guest3450132 Nov 23 '18 at 09:09
  • What did you mean by "as abstract class" in the original question? – Peter Ruderman Nov 23 '18 at 12:36
  • Abstract means, that in order to avoid misuse of the CRTP base class, it should not be instantiable without a dreived class. To prevent a wrong inheritance (a derived class that gives a wrong derived type to the CRTP) you need to make the constructors private and befriend the derived class. This way only the derived class can call the base class constructors and thus can be instantiated. – guest3450132 Nov 30 '18 at 09:21