C++如何获取模板函数实例的地址?

10

抱歉,这段代码有些复杂(在此处也是如此)


#include <memory>
#include <cstdio>

template< typename T, size_t N, typename ... Args >
 void test_1 ( Args ... args)
 {
    using namespace std;

    if constexpr( 1 > (sizeof... (args))  ) {
         return;
    }
    else 
    {
    auto    arg_list [[maybe_unused]] = { args ... };
    }
 }
    template< typename T, size_t N, typename ... Args >
    void test_2 ( Args ... args)
    {
      auto  arg_list [[maybe_unused]] = { args ... };
    }
  ///////////////////////////////////////////////////////////
 int main()
 {
     test_1<int,3>(1,2,3);
     test_2<int,3>(1,2,3);

// taking the function pointer (aka address of) leads to
// full instantiation of function template 
constexpr auto adr_1 [[maybe_unused]] = std::addressof(test_1<int,3>);
// no can do --> constexpr auto adr_2 = std::addressof(test_2<int,3>);

}

因此,从上面的内容可以看出,获取模板函数指针(即地址)会导致函数模板的完全实例化。

这有点不幸。如果获取模板函数实例化的地址,那么将产生一个完全实例化。即使永远不会调用该函数。

     // pointer to instance made but instance never used
     constexpr auto adr_5 [[maybe_unused]] = std::addressof(test_1<float,9>);
     // pointer to instance made but instance never used
     constexpr auto adr_of_big_foot_gun 
           [[maybe_unused]] = std::addressof(test_1<bool,99>);
     // pointer to instance made but instance never used
     constexpr auto adr_of_crazy [[maybe_unused]] = 
                 std::addressof(test_1<char, 0xFFFF>);

现在,如何绕过这个问题呢?请注意上述代码意味着编译器会执行以下操作:
        // this line
        std::addressof( test_2<int,3> )

        // provokes something like this
        // which needs to be compiled
        test_2<int,3>(void) ;

这就是为什么我们可以获取上面的test1地址,但不能获取test_2的原因。对我来说,这根本没有任何意义。而且,具体来说,这意味着编译函数模板实例,好像使用空参数调用它一样?换句话说,如果没有constexpr-if,那么很难编写上面的test_1。这又(几乎)意味着没有c++11和c++14。
请讨论...

无法绕过这个问题。必须存在某些东西才能有地址。您期望未实例化的函数模板特化的地址是什么? - Michael Kenzel
所以,你想避免实例化,但你期望什么指针值呢?你只需要函数类型吗? - ivaigult
你是否试图进行像“函数指针被调用时实例化函数”这样的优化?如果是这样,那么这是不可能的,因为C++只在编译时执行这些技巧。如果你可以接受adr_1 == nullptr,那么只需将你的函数指针类型进行typedef并将其赋值为nullptr即可。 - ivaigult
是的,这正是编译器中非常不错的功能。 - user10133158
请注意std::addressof(test_1<int,3>) != std::addressof(test_1<int,3,char>),因此无效的test_2<int,3>地址无论如何都是无用的。 - Jarod42
2个回答

6

这有点不幸。如果一个模板函数实例化的地址被获取,那将会产生完整的实例化。

这并不是不幸的事情。这就是语言的工作方式。如果您获取模板函数的地址(从函数模板实例化的函数),那么该函数必须存在。换句话说,它必须从相应的函数模板实例化。

如何规避这个问题?

没有办法。这是一种预期行为,完全合理。


感谢您的回答。也许对我来说,这根本没有任何意义。我的意思是编译有趣的模板实例,就好像使用void参数调用它一样。这又意味着,如果没有constexpr-if,那么很难编写上面的test_1。这又(几乎)意味着没有c++11和c++14。 - user10133158
1
@Edgar Rokjan "你不能专门化函数模板"。我认为你可以?https://de.cppreference.com/w/cpp/language/template_specialization - phön
@phön 我丢失了一个重要的单词:部分地。非常感谢您的审查! - Edgar Rokjān
@phön,我已经从问题中删除了“专业化”一词,因为在Edgar正确的上下文中,它是不相关的使用情况。 - user10133158
3
函数模板本质上只是一个模板,在实例化之前并不存在于真正的程序中。你无法取得不存在的东西的地址。如果您想要实例化特定版本的模板函数,可以传递这些类型参数: test_2<int, 3, char, double, int> 等等。 - Michał Łoś
显示剩余3条评论

1
为了获取一个地址,你需要完整的实例化,否则就没有指向的对象。请记住,在运行时编译器不会完成部分参数化模板的生成。即使在仅知道函数指针的不同调用点上,编译器也无法实例化模板,因为它可能不知道该模板。 test2<int, 3, ...>(或者同样的test1)的实例是不同的函数,取决于传递的参数,它们永远不可能有一个通用的地址。例如,test2<int,3, int>test2<int, 3, int, int> 是不同的,它们接受不同的参数并对其执行不同的操作。它们的地址不同,相应的函数指针类型也不同。
根据你使用地址的方式,你可能可以通过模板参数化(如果指针本身是被实例化或未被实例化的模板的一部分)或constexpr-if结构在编译时将它们关闭。

如果所有的对象文件中都没有指针的引用,链接器有可能会通过正确的选项删除不必要的实例化模板,但这并不是保证的,并且编译时间仍然会受到影响。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接