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;
}