c ++中的extern模板构造函数

时间:2013-02-21 16:23:12

标签: c++ templates c++11 constructor extern

很长一段时间以来,我一直在努力解决将模板化函数的实现与定义分离的old-as-c ++问题。 C ++ 0x'extern似乎是一个解决方案,但我无法正确应用

我的代码:

main.cpp

#include <iostream>
#include <string>

#include "lexer.h"

int main(int argc, char const *argv[]) {
    std::string foo("foo");
    new lexer((foo.begin()),(foo.end()));
    return 0;
}

lexer.h

#ifndef lexer_h
#define lexer_h
class lexer {
    public:
    extern template<class InputIterator>
    lexer(InputIterator i, InputIterator end);
};
#endif //lexer_h

lexer.cpp

#include "lexer.h"
template<class InputIterator >
lexer::lexer(InputIterator i, InputIterator end) {
    //make it work
};

使用g++ main.cpp lexer.cpp -std=c++0x进行编译。我想稍后使用目标文件。

那怎么看起来固定不变?

2 个答案:

答案 0 :(得分:1)

除非您知道用作InputIterator参数的完整类型集,否则定义需要进入头文件。

当您知道所需的完整实例集(参数)时,模板定义(实现)只能与声明分开。编译器无法记住在一个.cpp(翻译单元)中使用了哪些实例,并使用另一个.cpp中的代码提供它们。

正如Andy所提到的,你似乎要寻找的行为以前被分配到C ++ 03 export关键字,这个关键字很少实现,结果证明它没有希望的那么有用,现在已经完全从标准中删除。

如果您确实想要走这条路(我现在正在编写类似的库!),extern关键字需要超出class {}范围和.cpp文件需要明确地实例化所需的专业化。

// header file

class lexer {
    public:
    template<class InputIterator>
    lexer(InputIterator i, InputIterator end);
};

extern template lexer::lexer( foo::iterator, foo::iterator );
extern template lexer::lexer( bar::iterator, bar::iterator );

// source file

template<class InputIterator >
lexer::lexer(InputIterator i, InputIterator end) {
    //make it work
};

template lexer::lexer( foo::iterator, foo::iterator );
template lexer::lexer( bar::iterator, bar::iterator );

答案 1 :(得分:0)

它不起作用的原因是你没有实例化你的类模板,你也没有实例化你的类模板的构造函数:处理除.cpp以外的翻译单元(即lexer.cpp文件)如果调用该构造函数,编译器将无法看到它的定义,因此它不会为它发出任何对象代码;另一方面,在可以看到其定义(lexer.cpp)的唯一转换单元中,您没有调用构造函数,因此编译器也不会发出任何对象代码。

因此,您的程序编译的翻译单元中不存在构造函数的目标代码,并且链接器会在尝试创建可执行文件时向您的类的构造函数抱怨未解析的引用

当您知道时,extern关键字用于阻止在一个翻译单元中实例化模板(即使其完整定义可见!)它将(明确地)在另一个翻译单元中实例化,从而节省编译时间。有关说明,请参阅 this Q&A on StackOverflow

来自C ++ 11标准的第14.7.2 / 2段:

  

显式实例化的语法是:

     

显式实例:   extern(opt)模板声明

     

显式实例化有两种形式:显式实例化定义和显式实例化声明。显式实例化声明extern关键字开头。

因此,您提供的只是一个声明。除非您在同一个翻译中实例化它们(可能通过类模板的显式实例化),否则无法在.cpp文件中降级类模板的成员函数定义而不会从链接器获取未解析的引用错误unit(唯一可以访问这些定义的人)。

C ++ 03有一个名为export的关键字允许执行你想要实现的目标,但是在C ++ 11的标准化过程中it has been removed因为它被证明对编译器实现起来太困难了供应商。