是否有可能获得函数模板的隐式实例化的地址?

时间:2015-04-08 18:39:41

标签: c++ templates c++11

我想知道是否有任何方法可以获取由一组特定参数生成的函数模板的实例化地址。

#include <iostream>

template <typename T>
class A {}; 

template <typename T, typename T2>
int hello(A<T> a, A<T2> b, int c)
{
    return 69; 
}

int main()
{
    A<int> a;
    A<float> b;
    std::cout << (&hello)(a, b, 3) << "\n";                                                                                                                                                           

    return 0;
}

此代码打印函数调用返回的值。如何打印为a和b参数实例化的“hello”版本的地址?我希望编译器可以推断出这些类型。

3 个答案:

答案 0 :(得分:8)

根据参数确定要调用的函数的过程称为重载解析,以及使用它的情况下的标准列表:

  

13.3重载分辨率[over.match]

     

2重载分辨率选择要在语言中的七个不同上下文中调用的函数:

     

(2.1) - 调用函数调用语法(13.3.1.1.1)中指定的函数;

     

(2.2) - 在类对象上调用函数调用操作符,指向函数的指针转换函数,指向函数的引用转换函数或函数引用转换函数命名   在函数调用语法(13.3.1.1.2);

     

(2.3) - 调用表达式(13.3.1.2)中引用的运算符;

     

(2.4) - 调用类对象(13.3.1.3)的直接初始化(8.5)的构造函数;

     

(2.5) - 为类对象的复制初始化(8.5)调用用户定义的转换(13.3.1.4);

     

(2.6) - 从类类型的表达式(13.3.1.5)调用转换函数以初始化非类类型的对象;和

     

(2.7) - 调用转换函数以转换为glvalue或类prvalue,引用(8.5.3)将直接绑定到该值(13.3.1.6)。

其中,适用于常规函数的一个是2.1,这需要一个f(args)上下文,它只告诉调用者结果。

所以,你要求的是无法做到的。不完全是这样。

现在,根据你想要完成的事情, 可能会有一些事情:

如果您知道完全签名,则可以获得指向该函数的指针:给定template <typename T> int hello(A<T> a, A<T> b),您可以使用static_cast<int(*)(A<int>,A<int>)>(hello)获取地址。但是,要使其工作,您需要知道返回类型(您可以使用decltype获得),并且您需要知道参数类型(可能与参数类型不同,哪些你无法可靠地获得。)

也可以获得一个指向函数的指针,该函数在被调用时将具有与hello相同的效果:

auto callable = +[](A<int> a, A<int> b) { return hello(a, b); };

[](A<int> a, A<int> b) { return hello(a, b); }创建一个没有任何捕获的lambda,没有任何捕获的lambda可以隐式转换为匹配类型的函数指针。 +强制使用该转换,而不需要拼写出类型。

但是,这与hello的地址不同,因此可能不适合后续比较。

这是你能得到的最好的。

答案 1 :(得分:2)

我在Ideone尝试了这个:

// A and hello defined as OP
int main()
{
    A<int> a;
    A<float> b;

    decltype(hello(a,b,3)) (*pf)(decltype(a), decltype(b), decltype(3))=hello;
    std::cout << (void*)pf << "\n";                                                                                                                                                           

    return 0;
}

它似乎输出了一个内存地址。

答案 2 :(得分:1)

@hvd:AFAIK,你不能在不知道你想要包装的函数的签名的情况下声明lambda(lambdas不能被模板化)。但您可以使用静态方法的中间类:

#include <functional>
#include <iostream>

template<class A>
void func(A a, int b)
{
    std::cout << "a=" << a << " b=" << b << "\n";
}

template<class... A>
class proxy_func
{
public:
    static auto call(A... args) -> decltype(func(args...))
        {
            return func(args...);
        }
};

template<template<class...> class P, class... User>
void* addr(User... user)
{
    return (void*)&P<User...>::call;
}

template<template<class...> class P, class... User>
auto call(User... user) -> decltype(P<User...>::call(user...))
{
    return P<User...>::call(user...);
}

template<class T>
void test()
{
    T value = 1;
    printf("func > %p\n", &func<T>);
    printf("func > ");
    func(value, 1);
    printf("proxy> %p\n", &proxy_func<T, int>::call);
    printf("proxy> ");
    proxy_func<T, int>::call(value, 1);
    printf("auto > %p\n", addr<proxy_func>(value, 1));
    printf("auto > ");
    call<proxy_func>(value, 1);
}

int main(int argc, char **argv)
{
    printf("==int==\n");
    test<int>();
    printf("==long==\n");
    test<long>();
}

结果如下:

g++ -std=c++11 -o /tmp/test /tmp/test.cpp && /tmp/test
==int==
func > 0x400a8d
func > a=1 b=1
proxy> 0x400ae6
proxy> a=1 b=1
auto > 0x400ae6
auto > a=1 b=1
==long==
func > 0x400b35
func > a=1 b=1
proxy> 0x400b91
proxy> a=1 b=1
auto > 0x400b91
auto > a=1 b=1

当然,这需要声明一个知道目标函数的 name 的通用代理(没有别的),这可能不被接受为@JavierCabezasRodríguez的解决方案。

PS:对不起,我没有将其作为评论发布,但我没有足够的声誉。

修改

使用代理类而不是lambda放弃了需要知道参数的数量,这样你就可以使用一种hackishly宏方法来包装任何目标函数:

#define proxy(f)                                \
    template<class... A>                        \
    class proxy_ ## f                           \
    {                                           \
    public:                                                     \
        static auto call(A... args) -> decltype(f(args...))     \
        {                                                       \
            return f(args...);                                  \
        }                                                       \
    }

proxy(func);