Как передать указатель функции, указывающий на конструктор?

Я работаю над реализацией механизма отражения на С++. Все объекты в моем коде являются подклассом Object (мой собственный универсальный тип), который содержит статический элемент данных типа Class.

class Class{
public:
   Class(const std::string &n, Object *(*c)());
protected:
   std::string name;     // Name for subclass
   Object *(*create)();  // Pointer to creation function for subclass
};

Для любого подкласса объекта со статическим элементом данных класса я хочу иметь возможность инициализировать «создать» указателем на конструктор этого подкласса.


person Kareem    schedule 05.06.2009    source источник
comment
Хотя это 6 лет спустя, вам следует хорошенько подумать о том, действительно ли вы хотите реализовать свой собственный механизм отражения. Сначала рассмотрите возможность «отражения» во время компиляции с использованием шаблонов, type_traits и принципа SFINAE; затем попробуйте одну из существующих библиотек отражения C++; и только тогда я бы подумал о том, чтобы попробовать это самому.   -  person einpoklum    schedule 16.12.2015


Ответы (7)


Вы не можете взять адрес конструктора (Стандартные конструкторы С++ 98 12.1/12 — «Конструкторы 12.1-12 — «Адрес конструктора не должен приниматься».)

Лучше всего иметь фабричную функцию/метод, которая создает Object и передает адрес фабрики:

class Object;

class Class{
public:
   Class(const std::string &n, Object *(*c)()) : name(n), create(c) {};
protected:
   std::string name;     // Name for subclass
   Object *(*create)();  // Pointer to creation function for subclass
};

class Object {};

Object* ObjectFactory()
{
    return new Object;
}



int main(int argc, char**argv)
{
    Class foo( "myFoo", ObjectFactory);

    return 0;
}
person Michael Burr    schedule 05.06.2009
comment
Если сделать его шаблоном, то он фактически вернет Class: template‹typename T› Object* ObjectFactory() { return new T; } .... Класс foo(myFoo, &ObjectFactory‹Class›); - person Johannes Schaub - litb; 05.06.2009
comment
Причина: stackoverflow.com/questions/7905272/ - person Rufus; 18.02.2020

Я столкнулся с этой же проблемой. Моим решением была функция шаблона, которая вызывала конструктор.

template<class T> MyClass* create()
{
    return new T;
}

Использовать это как указатель на функцию просто:

MyClass* (*createMyClass)(void) = create<MyClass>;

И чтобы получить экземпляр MyClass:

MyClass* myClass = createMyClass();
person Andrew Wansink    schedule 26.05.2017

Лямбда-стиль:

[](){return new YourClass();}
person kungfooman    schedule 11.12.2016
comment
Пожалуйста, рассмотрите возможность добавления дополнительных объяснений/контекста для этого, так как это сделает ответ более полезным для будущих читателей. - person EJoshuaS - Reinstate Monica; 11.12.2016
comment
Обязательно упомяните, что это функция C++ 11 и более поздних версий. - person Kareem; 15.12.2016
comment
вы можете опустить () - person Jimmy R.T.; 29.01.2021
comment
Это определенно самое чистое решение. Вы даже можете использовать более сложные шаблоны для определения одной лямбды, которая работает со всеми конструкторами всех классов. - person Smiley1000; 21.07.2021

Хм, странно. create - это переменная-член, т.е. доступная только в экземплярах класса, но ее цель, по-видимому, заключается в создании экземпляра в первую очередь.

Вы не можете взять адрес конструктора, но вы можете создать собственные статические фабричные методы и взять его адрес.

person laalto    schedule 05.06.2009
comment
laalto, Когда я создаю объект класса, я передаю имя и функцию создания. Моя цель - иметь список классов, на которые я могу ссылаться в любом месте моего проекта. Указатель на статическую функцию-член будет работать. - person Kareem; 05.06.2009

Вы не можете использовать обычные указатели функций для методов, вы должны использовать указатели методов, которые имеют странный синтаксис:

void (MyClass::*method_ptr)(int x, int y);
method_ptr = &MyClass::MyMethod;

Это дает вам указатель метода на метод MyClass — MyMethod. Однако это не настоящий указатель, поскольку это не абсолютный адрес памяти, это в основном смещение (более сложное, чем из-за виртуального наследования, но это зависит от реализации) в классе. Таким образом, чтобы использовать указатель метода, вы должны снабдить его классом, например:

MyClass myclass;
myclass.*method_ptr(x, y);

or

MyClass *myclass = new MyClass;
myclass->*method_ptr(x, y);

Конечно, в этот момент должно быть очевидно, что вы не можете использовать указатель метода, чтобы указать на конструктор объектов. Чтобы использовать указатель метода, вам нужно иметь экземпляр класса, чтобы его конструктор уже был вызван! Так что в вашем случае предложение Michael's Object Factory, вероятно, лучший способ сделать это.

person Niki Yoshiuchi    schedule 05.06.2009
comment
Это неправильно. &MyClass::MyMethod дает вам адрес в памяти фактического исполняемого кода MyMethod. Методы имеют неявный первый аргумент, указатель this. Вы не предоставляете методу класс, вы предоставляете ему объект (экземпляр класса, например указатель this). Поищите соглашение о вызовах thiscall. - person Rob K; 05.06.2009
comment
Я должен добавить, что получение местоположения кода для вызова, если метод виртуальный, требует больше магии, но существование методов никоим образом не зависит от существования любого экземпляра класса. - person Rob K; 05.06.2009
comment
А, так ты прав. Концептуально это не имеет большого значения для OP, в любом случае указатель метода не будет работать, если не существует экземпляра класса. Я исправил свой пост, чтобы отразить это. - person Niki Yoshiuchi; 06.06.2009

Используя вариативные шаблоны, вы можете создать оболочку, которая превратит конструктор в функтор.

#include <utility>

template <typename T>
struct BindConstructor{
    template<typename... Args>
    T operator()(Args...args)const{
        return T(std::forward<Args>(args)...);
    }
};


struct Foo {
    Foo(int a, int b):a(a),b(b){}
    int a;
    int b;
};

template <typename Fn>
auto Bar(Fn f){
    return f(10,20);
}

int main(){
    Foo foo = Bar(BindConstructor<Foo>());
}

https://godbolt.org/z/J6udk7

person bradgonesurfing    schedule 16.06.2020

Используя Qt, вы можете вызвать конструктор с механизмами отражения Qt (QMetaObject), если вы объявите конструктор как Q_INVOKABLE (больше ничего не нужно делать).

class MyClass : public QObject {
   Q_OBJECT
public:
   Q_INVOKABLE MyClass(int foo);
   MyClass *cloningMySelf() {
     return metaObject()->newInstance(Q_ARG(int, 42));
   }
};

Я не уверен, что вы захотите встроить Qt только для этой функции ;-), но, может быть, вы хотели бы посмотреть, как он это делает.

http://doc.qt.io/qt-5/metaobjects.html#meta-object-system

person Jim    schedule 03.09.2015