Basically I want all sub-types to be created through a factory method (I have a tall domain hierarchy with some 200+ classes).
For new
, it is not an issue since this can be overridden in A (making new
private).
class A{
protected:
A();
public:
template<class T, typename... ARGUMENTS>
static T* create(ARGUMENTS&&... arguments);
};
class B : public A {
public:
B();
};
void test() {
B b;//compile error wanted here - but as a consequence of inheriting A
}
Here A is a the "library/framework" class. Whereas B is a "user created class". It might be ok to require a typedef or similar on B.
UPDATE: I added the 'create' function on A that I intent to use for creating objects.
You can require a token in the construction of A
that is only passed in the body of A::create
#include <utility>
class A{
private:
struct create_token
{
create_token(const create_token &) = delete;
create_token& operator=(const create_token &) = delete;
create_token(create_token &&) = default;
create_token& operator=(create_token &&) = default;
};
protected:
A(create_token) {}
public:
template<class T, typename... ARGUMENTS>
static T* create(ARGUMENTS&&... arguments)
{
// Whatever creation mechanism here
return new T(create_token{}, std::forward<ARGUMENTS>(arguments)...);
}
};
class B : public A {
public:
template <typename Token> // Can't name A::create_token, it is private
B(Token tok) : A(std::move(tok)) {}
B(){} // Will always lack a `create_token`
};
int main() {
B b;//compile error wanted here - but as a consequence of inheriting A
B* b = A::create<B>();
}
See it live
This is another approach which relies on checking if the derived class constructor is private. But honestly, I prefer the solution given by @Caleth
#include <type_traits>
#include <iostream>
#include <type_traits>
template<typename T, typename... Args>
struct constructor_tag{};
class A{
protected:
template<typename T, typename... Args>
A(constructor_tag<T, Args...>) {
static_assert(!std::is_constructible_v<T, Args...>, "CONSTRUCTOR MUST NOT BE PUBLIC");
};
public:
template<class T, typename... ARGUMENTS>
static T* create(ARGUMENTS&&... arguments) {
return new T(std::forward<ARGUMENTS>(arguments)...);
}
};
class B : public A {
friend class A;
B() : A(constructor_tag<B>{}) {}
public:
};
class C : public A {
friend class A;
C () : A(constructor_tag<C>{}) {}
C(int) : A(constructor_tag<C, int>{}) {}
public:
};
// Following class will not compile because the constructor is public
//class D : public A {
// friend class A;
//
//public:
// D () : A(constructor_tag<D>{}) {}
//
//};
void test() {
// B b; //calling a private constructor of class 'B'
// C c(5);//calling a private constructor of class 'A'
A::create<B>();
A::create<C>(5);
A::create<C>();
}
int main() {
test();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With