C++ 具有不同签名的函数数组

C++ 具有不同签名的函数数组,c++,function-pointers,c++11,method-signature,C++,Function Pointers,C++11,Method Signature,我有以下课程: class Foo { ... }; class Foo1 : public Foo { ... }; ... class FooN : public Foo { ... }; 是否可能有一系列具有此类签名的函数: void f1(Foo1*){} ... void fN(FooN*){} 如果这些函数是非静态成员函数而不是常规函数,是否有任何更改?我认为这不会改变什么 谢谢 编辑备选的非虚拟功能解决方案 类型void(*)(Foo*)不能转换为

我有以下课程:

class Foo
{
    ...
};

class Foo1 : public Foo
{
    ...
};

...

class FooN : public Foo
{
    ...
};
是否可能有一系列具有此类签名的函数:

void f1(Foo1*){}
...
void fN(FooN*){}
如果这些函数是非静态成员函数而不是常规函数,是否有任何更改?我认为这不会改变什么


谢谢

编辑备选的非虚拟功能解决方案

类型
void(*)(Foo*)
不能转换为类型
void(*)(Bar*)
,这是有充分理由的

您应该使所有函数都采用
Interface*
参数,并且所有
FooN
都应该派生自
Interface

struct Interface {
    virtual ~ Interface () {}
    // ...
};

struct Foo1 : public Interface {
    // ...
};

struct Foo2 : public Interface {
    // ...
};

void f1 (Interface *);
void f2 (Interface *);

void (*functions)(Interface*) [] = {f1, f2};

functions[0] (new Foo1 ());
functions[0] (new Foo2 ());
functions[1] (new Foo1 ());
functions[1] (new Foo2 ());
f1
f2
的实现可以通过使用
dynamic_cast
并检查
nullptr
在运行时检查其参数是否为特定实现。编译时检查的唯一方法是使
f1
f2
采用特定类型,而不是将它们放入匿名数组中,而是显式调用它们



回答问题的第二部分——是的,如果它们是非静态成员函数,这很重要,因为您可以使用函数对象


例如Boost.Signal或来自C++0x/TR1的函数

您可以使函数f1通过其特定参数类的fN成员,命名相同的函数,并使用虚拟分派来调用正确的函数。然后您只需将指向成员函数的指针填充到数组中。

C++是一种静态类型语言,其中包括函数类型。在每一行代码中,C++编译器必须能够确定函数签名是否有效以及调用哪个函数(或指针)。 为了实现您所说的,您需要能够在运行时根据运行时放入数组的值恢复指针的类型。多态性是运行时唯一可以得到的与类型相关的东西。即使这样,也只涉及阶级的类型。究竟调用哪一个函数没有争议

您所能做的最好的事情就是使用类似于
boost::variant
的数组。您可以使用
boost::function
将一组特定的函数原型存储在变体中。但是,它将只是一个有界集,而不是任何任意函数类型。调用它们会相当困难,因为您首先必须验证变量确实是预期的函数类型,然后调用它

另一种选择是使用
boost::any
数组。除此之外,类型可以是任何函数类型。同样,调用它需要将其转换为预期的函数类型之一。问题更加复杂,因为函数类型可以是任何类型。因此,如果不是预期的函数类型之一,则必须提供回退

如果函数列表很小且编译时间确定,则可以使用
boost::tuple
作为临时“数组”。但是,您必须使用模板元编程来迭代它们。当然,如果是这样的话,您可以使用包含适当类型的函数指针的结构。

您需要的是。C++中不支持这一点,因为它破坏了类型安全性。为了更好地理解这一点,让我们举一个简单的例子:

struct Vehicle {};
struct Tricycle : Vehicle {};
struct Tank : Vehicle {};

void drive(Vehicle const & b) { ... }
void giveToChild(Tricycle const & b) { ... }
这里我们有一个简单的类型层次结构,以及两个分别引用基类和一个派生类的函数。现在,如果你的要求被允许,我们可以做以下事情:

typedef void (*funcPtr)(Vehicle const &);

funcPtr = &giveToChild; // this is not allowed
funcPtr(Tank());        // oops, I just gave a tank to my child!
<>语言可以实现某种类型的运行时类型验证,但这不是C++如何工作的。 然而,反向转换(逆变)可以不受任何问题的限制(实际上,C代表允许它),但是在C++中是不可能的,因为我不知道的一些原因。以下是它将允许的示例:

typedef void (*funcPtr)(Tricycle const &);

funcPtr = &drive;    // this could be allowed, but is not (in C++)
funcPtr(Tricycle()); // I can only drive a tricycle, but that's ok since it's a
                     // vehicle and I know how to drive all vehicles
因此,基本上,如果不使用转发函数(在调用原始函数之前检查参数的类型),您试图实现的目标是不可能的:

void forwardFN(Foo * f)
{
    FooN * instance = dynamic_cast<FooN *>(f);

    if (instance) fN(instance);
    else throw type_exception();
}
void forwardFN(Foo*f)
{
FooN*instance=dynamic\u cast(f);
if(实例)fN(实例);
else抛出类型_异常();
}

我找到了解决此问题的方法:

#include <iostream>
#include <vector>

class Foo
{
};

class Foo1 : public Foo
{
};

class Foo2 : public Foo
{
};

class Foo3 : public Foo
{
};


void f1(Foo1*)
{
    std::cout<<"f1\n";
}

void f2(Foo2*)
{
    std::cout<<"f2\n";
}

void f3(Foo3*)
{
    std::cout<<"f3\n";
}

template<typename T>
void AddPointer(std::vector<typename void (*)(Foo*)>& fPointers, T function)
{
    fPointers.push_back(reinterpret_cast<void (*)(Foo*)>(function));
}

void main()
{
    std::vector<typename void (*)(Foo*)> fPointers;

    AddPointer(fPointers, f1);
    AddPointer(fPointers, f2);
    AddPointer(fPointers, f3);

    Foo1 foo1;
    Foo2 foo2;
    Foo3 foo3;

    fPointers[0](&foo1);
    fPointers[1](&foo2);
    fPointers[2](&foo3);
}
#包括
#包括
福班
{
};
Foo1类:公共Foo
{
};
Foo2类:公共Foo
{
};
Foo3类:公共Foo
{
};
无效f1(Foo1*)
{

STD::CUT

你可以使用函数对象。下面的例子就可以说明如何自己做。如果你喜欢这个想法,你应该看看BooS.Stulal.BooSt.Band和C++ 0x对应。

class Foo1 {};
class Foo2 {};
class Foo3 {};

void func1(Foo1*) {}
void func2(Foo2*) {}
void func3(Foo3*) {}

class FuncObjBase {
public:
    virtual void operator()() = 0;
};

template <class T>
class FuncObj : public FuncObjBase {
public:
    typedef void (*Funcptr)(T*);
    FuncObj(T* instance, Funcptr funcptr) : m_Instance(instance), m_Func(funcptr) {}
    virtual void operator()() { m_Func(m_Instance); }
private:
   T* m_Instance;
   Funcptr m_Func;
};

int main(int argc, char *argv[])
{
    Foo1 foo1;
    Foo2 foo2;
    Foo3 foo3;
    FuncObjBase* functions[3];
    functions[0] = new FuncObj<Foo1>(&foo1, func1);
    functions[1] = new FuncObj<Foo2>(&foo2, func2);
    functions[2] = new FuncObj<Foo3>(&foo3, func3);
    for(unsigned int i = 0; i < 3; i++) {
        (*functions[i])();
    }
    return 0;
}
class Foo1{};
类Foo2{};
第3类{};
void func1(Foo1*){}
void func2(Foo2*){}
void func3(Foo3*){}
类FuncObjBase{
公众:
虚空运算符()()=0;
};
模板
类FuncObj:public FuncObjBase{
公众:
类型定义无效(*Funcptr)(T*);
FuncObj(T*instance,Funcptr Funcptr):m_instance(instance),m_Func(Funcptr){
虚void操作符()({m_Func(m_实例);}
私人:
T*m_实例;
Funcptr mu Func;
};
int main(int argc,char*argv[])
{
Foo1 Foo1;
Foo2 Foo2;
Foo3-Foo3;
FuncObjBase*函数[3];
函数[0]=新的FuncObj(&foo1,func1);
函数[1]=新的FuncObj(&foo2,func2);
函数[2]=新的FuncObj(&foo3,func3);
for(无符号整数i=0;i<3;i++){
(*职能[i])();
}
返回0;
}

我建议使用
std::tuple
而不是
std::array
或C-array。使用
std::tuple
可以存储不同类型的元素。

以下是一种通用方法,它是类型安全的,并强制客户端代码正确

class Manager {
public:

    typedef int /* or whatever */ ID;

    template <typename Function>
    static void save (Function * f, ID id) {
        functions <Function> () .add (id, f);
    }

    template <typename Function>
    static Function * get (ID id) {
        return functions <Function> () .get (id);
    }

private:

    template <typename Function>
    class FunctionStore {
    public:

         void add (Function *, ID);
         Function * get (ID);

    private:
         // or vector, if you know ID is int.
         std :: map <ID, Function *> m_functions;
    };

    // type_index is C++11 but you can implement it in C++03.
    // void* here is unpleasant but you can improve it, RAII it.
    typedef std :: map <std :: type_index, void *> Store;
    static Store m_store;

    template <typename Function>
    FunctionStore <Function> & functions () {
        FunctionStore <Function> * fs;

        Store :: iterator i = m_store .find (typeid Function);

        if (m_store .end () == i) {
            fs = new FunctionStore <Function> ();
            m_store [typeid Function] = fs;
        }
        else {
            // This void* cast is OK because it's internally controlled
            // and provably correct.
            // We don't have to trust the library to not abuse it.
            fs = static_cast <FunctionStore<Function>*> (i -> second);
        }

        return *fs;
    }
};

// In the library

void foo1 (Foo *);
void bar1 (Bar *);
void foo2 (Foo *);
void bar2 (Bar *);

void init () {
    Manager :: save (foo1, 1);
    Manager :: save (foo2, 2);
    Manager :: save (bar1, 1);
    Manager :: save (bar2, 2);

    Manager :: get <void(Foo*)> (1) (new Foo ()); // OK, calls foo1
    Manager :: get <void(Foo*)> (1) (new Bar ()); // Will not compile
    Manager :: get <void(Bar*)> (2) (new Bar ()); // OK, calls bar2
}
init
函数说明了我在其他帖子的评论中提到的一个关键点:如果您知道要使用哪些类型调用函数,那么您就知道要从哪个函数集合中获取
void init () {
    Manager <void(Foo*)> :: save (foo1, 1);
    Manager <void(Foo*)> :: save (foo2, 2);
    Manager <void(Foo*)> :: save (bar1, 1); // Won't compile
    Manager <void(Bar*)> :: save (bar1, 1);
    Manager <void(Bar*)> :: save (bar2, 2);

    Manager <void(Foo*)> :: get (1) (new Foo ()); // OK, calls foo1
    Manager <void(Foo*)> :: get (1) (new Bar ()); // Will not compile
    Manager <void(Bar*)> :: get (2) (new Bar ()); // OK, calls bar2
}