Можно ли выдать ошибку компиляции, если объект создан стеком (включая унаследованные типы)? - PullRequest
0 голосов
/ 07 января 2019

По сути, я хочу, чтобы все подтипы создавались с помощью фабричного метода (у меня высокая иерархия доменов с более чем 200 классами).

Для new это не проблема, так как это может быть переопределено в A (делая new приватным).

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
}

Здесь A - это класс "библиотека / фреймворк". Принимая во внимание, что B - «созданный пользователем класс». Вполне возможно, потребуется typedef или аналогичный для B.

ОБНОВЛЕНИЕ: Я добавил функцию «создать» на А, которую я намерен использовать для создания объектов.

Ответы [ 2 ]

0 голосов
/ 07 января 2019

Это еще один подход, который основывается на проверке, является ли конструктор производного класса закрытым. Но, честно говоря, я предпочитаю решение, данное @ 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();
}
0 голосов
/ 07 января 2019

Вы можете потребовать токен в конструкции A, который передается только в теле 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>();
}

Посмотреть вживую

...