外部定义的函数指针作为模板参数

时间:2012-01-21 23:03:05

标签: c++

请考虑以下代码:

main.cpp中:

#include <iostream>
typedef void ( * fncptr)(void);
extern void externalfunc(void);

template<void( * test)(void)>
class Bar
{
public:
    Bar() { test(); }
};

void localfunc()
{
    std::cout << "Hello World" << std::endl;
}
constexpr fncptr alias = localfunc;
extern fncptr externalAlias;
int main(int argc, char* argv[])
{
    Bar<localfunc> b;
    Bar<alias> b2; // This wouldn't compile if alias wasn't constexpr
    Bar<externalfunc> b3;
//  Bar<externalAlias> b4;

    return 0;
}

和external.cpp:

#include <iostream>

typedef void ( * fncptr)(void);

void externalfunc()
{
    std::cout << "Hello external world" << std::endl;
}

fncptr externalAlias = externalfunc;

现在的问题是我需要像主要功能中的第4行一样工作。我从外部C库获取这些函数声明,所以我无法触及它们。目前第4行没有编译。 gcc 4.6说“它必须是具有外部链接的函数的地址”。事实上,如果你使alias不是constexpr,它也会这样说,所以实际意义(我认为)应该解释为:“我不知道100%肯定你给我的函数地址是我需要实例化这个模板的常量“。有没有办法解决这个问题,因为我无法在externalalias中将constexpr声明为main.cpp

在你提出替代解决方案之前:我已经尝试通过将函数指针传递给构造函数并将它们保存在本地来完成这项工作但我仍然感兴趣,如果我可以让模板版本工作:)。 / p>

2 个答案:

答案 0 :(得分:2)

名称aliasexternAlias不是功能!它们是函数的指针,因此是可变的。您不能将可变对象用作模板参数,因为模板参数需要在编译时解析。但是你可以做两件事:

  1. 您可以创建一个包装器,使用您选择的任何名称来调用潜在的别名函数来提供包装器。如果功能签名之间存在某种程度的差异,这也可以方便地调整类型。
  2. 您可以使用指向函数指针的常量指针作为模板参数:通常还有一个更多级别的间接假设也适用于此。
  3. 所有这些都说,你究竟想要实现什么目标?你确定你不仅仅想要std::function<void(void)>这样的东西吗?我意识到这用于类型擦除,而你似乎有相反的目标,并将函数指针转换为唯一类型。但是,你的方式似乎并不是你打算做的事情。

    以下是第二个选项的示例:

    #include <iostream>
    
    extern void f(int);
    extern void g(int);
    
    void (*falias)(int) = f;
    void (*galias)(int) = g;
    
    template <void (**alias)(int)>
    struct foo
    {
        void bar() { (*alias)(17); }
    };
    
    void f(int x) { std::cout << "f(x)=" << x << "\n"; }
    void g(int x) { std::cout << "g(x)=" << x << "\n"; }
    
    int main()
    {
        foo<&falias>().bar();
        foo<&galias>().bar();
    }
    

    f()g()最终在同一翻译单元中实施的事实完全无关紧要:您可以将其移到其他地方而不会出现任何问题。

答案 1 :(得分:1)

如果您准备必须手动注释类型,这是可能的。最终,各种对象的类型略有不同。

我会向后写这个答案,先看main

int main(int argc, char* argv[])
{
    Bar<void(*)(void) , localfunc> b;
    Bar<void(**)(void), &alias> b2;
    Bar<void(*)(void) , externalfunc> b3;
    Bar<void(**)(void), &externalAlias> b4;

    return 0;
}

您可能需要考虑typeof(g ++)或decltype(c ++ 11)来创建稍微更具可读性的内容,并且可能使用宏:

Bar<decltype(&localfunc), localfunc> b;
Bar<decltype(&alias), &alias> b2;
Bar<decltype(&externalfunc), externalfunc> b3;
Bar<decltype(&externalAlias), &externalAlias> b4;

我不得不稍微改变Bar模板:

template<typename T, T t>
class Bar;

这样它就可以处理函数指针和指针 -to-function-pointers。这需要两个专业化:

template<void( * test)(void)>
class Bar<void(*)(void), test>
{
public:
    Bar() { std::cout << "* "; test(); }
};
template<void( ** test)(void)>
class Bar<void(**)(void), test>
{
public:
    Bar() { std::cout << "**"; (*test)(); }
};

应该可以为具有不同参数类型和返回类型的其他类型的函数添加更多特化,包括偶数方法调用。

我在自己的机器上完全测试了这一点。这是ideone上的演示(没有外部函数)。

注意:对于别名,template参数是别名的地址,因此它将考虑您在运行时对别名变量所做的任何更改。我猜你可能想把别名变成const。 const fncptr alias = localfunc;