2

I need to determine at compile time if an abstract class T (passed in as a template parameter) has a protected or private parameterless constructor. Because T is abstract, all the versions of the std::is_constructible<T> family return false no matter what.

I've tried defining a class U that inherits from the class in question, has using T::T, and is concrete; unfortunately, I've found the using directive ignores the default constructor;

(I need this for SFINAE purposes; I need to make my templated concrete-version-of-T factory refuse to work for T with explicitly inaccessible constructors.)

This is what I'm trying to make happen:

#include <iostream>
#include <utility>

template <typename T, typename... Args>
static T* make(Args&&...);

class Base {
    public:
        void ImportantFunctionThatRequiresFactorySupport();

    private:
        class InstantiationToken {};
        virtual InstantiationToken* NoInstantiationForYou() = 0;

        template <typename T>
        class Deabstractifier final : public T {
            private:
                InstantiationToken* NoInstantiationForYou() { return NULL; }
        };

    template <typename T, typename... Args>
    friend T* make(Args&&...);
};

template <typename T, typename... Args>
static T* make(Args&&... args) {
    // There should be a static_assert here to detect when T has a protected constructor and refuse to make one
    return new Base::Deabstractifier<T>(std::forward<Args>(args)...);
}

class IntermediateDerivedThatIsFineToConstruct : public Base {
    public:
        IntermediateDerivedThatIsFineToConstruct() = default;

        void DoSomethingOrdinaryAndCompletelyReasonable() {
            ImportantFunctionThatRequiresFactorySupport();
        }
};

class IntermediateDerivedThatShouldOnlyBeInheritedFrom : public Base {
    protected:
        IntermediateDerivedThatShouldOnlyBeInheritedFrom() = default;

    public:
        void SomethingElseCompletelyReasonable() {
            ImportantFunctionThatRequiresFactorySupport();
        }
};

class ThingThatShouldBeConstructibleAgain : public IntermediateDerivedThatShouldOnlyBeInheritedFrom {
    public:
        void SomeExtaFunctionality() {};
};


int main()
{
    std::cout << "Starting..." << std::endl;
    make<IntermediateDerivedThatIsFineToConstruct>(); // Should succeed
    std::cout << "Did thing 1" << std::endl;
    make<ThingThatShouldBeConstructibleAgain>(); // Should succeed
    std::cout << "Did thing 2" << std::endl;
    make<IntermediateDerivedThatShouldOnlyBeInheritedFrom>(); // Should fail at compile time
    std::cout << "Did thing 3" << std::endl;
}

live on coliru

Reid Rankin
  • 1,078
  • 8
  • 26
  • I don't think you can detect a private default constructor. For the protected, you might be able to do it by having a trait that inherits from the type, but then you have to also implement the pure virtual functions. If you gave a MCVE for how specifically this might be used, it would help in understanding how we could implement such a type trait. – Justin Jun 05 '17 at 04:27
  • 2
    This smells a bit like an [XY problem](https://meta.stackexchange.com/q/66377/218910). I'm having a hard time trying to come up with a scenario where it would possibly be useful to be able to detect this. What is your higher-level goal? – cdhowie Jun 05 '17 at 04:37
  • 1
    It's possible to distinguish between "no default constructor" and "has default constructor regardless of accessibility" by checking for ambiguity in overload resolution, and it's possible to distinguish between "has private default constructor" and "has public/protected default constructor" via a concrete derived class with a defaulted default constructor. I'd normally have written a (partial) answer, but since [you asked so nicely](https://stackoverflow.com/questions/40081920/inherited-constructors-default-constructor-and-visibility#comment75723870_40081920), I'll just shut up instead. – T.C. Jun 05 '17 at 06:21
  • @T.C. where has Reid asked in that question? I'm assuming he was being harsh, but where? – Johannes Schaub - litb Jun 05 '17 at 09:30
  • 2
    @T.C. my metaprogramming is a bit rusty these days, but this appears to work: http://coliru.stacked-crooked.com/a/46b552e02b63d616 – Johannes Schaub - litb Jun 05 '17 at 10:46
  • @T.C. Sorry; I probably should have added an emoji on that comment... it was intended to come across as complimentary and self-deprecating. In hindsight that's a lot easier to get across in person, and the way I worded it sounded mean. – Reid Rankin Jun 05 '17 at 15:49
  • @cdhowie You're absolutely right. What I really want is to guarantee that `shared_from_this()` will always return a non-empty shared_ptr, pointing to `this` *and owning the object*. That's a lot harder than it sounds. I even [asked a question](https://stackoverflow.com/questions/44349918/require-heap-allocation-for-any-derived-class) about part of it, and was scoffed at because what I wanted to do was over-engineered. I have a solution now, one that gets me most of the way there, but I need this last bit to perfect it. – Reid Rankin Jun 05 '17 at 16:07
  • @Justin Added MCVE. – Reid Rankin Jun 05 '17 at 16:33
  • 1
    @ReidRankin Is [this question](https://stackoverflow.com/q/12079711/501250) similar enough to yours that the answer could work for you? – cdhowie Jun 05 '17 at 16:37
  • @JohannesSchaub-litb That's some good work, but I need `CC(A2)` to return false. (I actually need to detect if the class has *any* publically accessible constructor (as in, I want `CC(A4)` to return true as well), but I already have a way to detect the accessibility of non-default constructors and I can just do an OR.) – Reid Rankin Jun 05 '17 at 17:35
  • @cdhowie It's taken me a few hours to understand that and play around with it, but unfortunately it does not work for me. That answer is a very clever way to detect constructors even if they are protected by access control, but what I want is to only detect constructors that are *not*. – Reid Rankin Jun 05 '17 at 20:08

0 Answers0