类模板专门化内部的静态成员函数

static member function inside class template specialization

本文关键字:静态成员 函数 内部 专门化      更新时间:2023-10-16

我正在努力访问类模板内定义的静态成员函数。在头文件TemplateTest.h中,我将主类Template定义为:

#include<iostream>
template<class T, class U>
struct TemplateTest
{
public:
    void static invoke();
    /*{
        std::cout << "Should not be called" << std::endl;
    }*/
};

源文件TemplateTester.cpp我放了一个专门化:

#include "TemplateTest.h"
template<>
struct TemplateTest<int, bool>
{
    static void invoke()
    {
        std::cout << "invoke<int, bool>" << std::endl;   
    }
};
template struct TemplateTest<int, bool>; //instantiate to resolve linker issue

我显式地实例化了这个类,使用了正确的链接器解析

driver.cpp:

include "TemplateTest.h"
int main()
{
    TemplateTest<int, bool>::invoke();
    return 0;
}

当我用g++编译TemplateTest.cpp时,它会正确生成对象文件,但当我试图将其链接到驱动程序类时,它会给我的链接器错误"未定义的引用到' templatettest::invoke()"

我浏览了其他相关的帖子,比如这个,但我没有尝试访问函数模板。

您从TemplateTester.cpp创建的对象文件将包含您提供的专门化的符号,这是正确的。之所以会出现这种情况,是因为任何显式专门化都会导致模板被实例化,而且由于您甚至添加了显式实例化(这实际上是不必要的),这种情况会加倍。

但是,在编译driver.cpp时,编译器并不知道专门化,因为您只包含了TemplateTester.h,并且在那里没有提到专门化。所以编译器实例化模板,当然不使用专门化定义,所以你得到你的问题。

The Standard says(斜体by me):

(§14.7.3/6)如果模板、成员模板或类模板的成员被显式特化,则该特化应在第一次使用该特化之前声明,该特化将导致隐式实例化发生,在每个使用该特化的翻译单元中不需要诊断。如果程序没有为显式专门化提供定义,并且专门化的使用方式导致隐式实例化发生,或者成员是虚成员函数,则程序是病态的,不需要诊断。对于声明但未定义的显式专门化,永远不会生成隐式实例化。[…]

因此,当编译器在driver.cpp上工作时,您需要同时声明和定义专门化。最好的方法是将整个专门化添加到TemplateTester.h

再次注意,实际上并不需要显式实例化。

有几个问题:

  • 你不需要显式实例化完全专门化的模板
  • 如果你想把你的静态方法放在头部,然后使用inline。否则你会得到多个实例和链接器问题
  • 模板专门化放在头文件中,并在源文件中定义方法
  • 如果你不希望某些东西在模板中被调用,你不需要定义它。你会得到编译错误,这意味着要更早地捕获错误。

// TemplateTest.h
#include<iostream>
template<class T, class U>
struct TemplateTest;
template<>
struct TemplateTest<int, bool>
{
    inline static void invoke()
    {
        std::cout << "invoke<int, bool>" << std::endl;   
    }
};
// main.cpp
include "TemplateTest.h"
int main()
{
    TemplateTest<int, bool>::invoke();
}

另一种方法是更改头文件,并添加源文件。

// TemplateTest.h
#include<iostream>
template<class T, class U>
struct TemplateTest;
template<>
struct TemplateTest<int, bool>
{
    static void invoke();
};
// TemplateTest.cpp
#include "TemplateTest.h"
void TemplateTest<int, bool>::invoke()
{
  std::cout << "invoke<int, bool>" << std::endl;   
}