简体   繁体   中英

extern template constructor in c++

for quite some time i struggle with old-as-c++ problem of separating the implementation of a templated function from the definition. C++0x' extern seem to be a solution for this, but i fail to apply it properly

my code:

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
};

compiling with g++ main.cpp lexer.cpp -std=c++0x . I want to use object files later.

so how would it look fixed?

Unless you know the complete set of types used as arguments to InputIterator , the definition needs to go into the header file.

A template definition (implementation) can only be separated from the declaration when you know the full set of instantiations (arguments) needed. The compiler cannot remember what instantiations were used in one .cpp (translation unit) and provide them using code in another .cpp .

As Andy mentions, the behavior you seem to be looking for was previously assigned to the C++03 export keyword, which was seldom implemented, and turned out to be less useful than hoped, and has now been completely removed from the standard.

If you do want to go this route (I'm writing a similar library right now!), the extern keyword needs to go outside the class {} scope and the .cpp file needs to explicitly instantiate the needed specializations.

// 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 );

The reason why it doesn't work is that you are not instantiating your class template, neither are you instantiating your class template's constructor: while processing translation units (ie .cpp files) other than lexer.cpp that invoke that constructor, the compiler won't be able to see its definition, so it will not emit any object code for it; on the other hand, in the only translation unit that can see its definition ( lexer.cpp ), you are not invoking the constructor, so again the compiler won't emit any object code.

As a result, no object code for your constructor is present in your program's compiled translation units, and the linker will complain about unresolved references to your class's constructor when trying to create the executable.

The extern keyword is used to prevent the instantiation of a template in one translation unit (even though its full definition is visible!) when you know that it will be (explicitly) instantiated in another translation unit, thus saving compilation time. See this Q&A on StackOverflow for a clarification.

From Paragraph 14.7.2/2 of the C++11 Standard:

The syntax for explicit instantiation is:

explicit-instantiation: extern(opt) template declaration

There are two forms of explicit instantiation: an explicit instantiation definition and an explicit instantiation declaration. An explicit instantiation declaration begins with the extern keyword.

Thus, what you are providing is simply a declaration. There is no way of relegating member function definitions of a class template in a .cpp file without getting unresolved reference errors from the linker, unless you instantiate them (possibly through an explicit instantiation of the class template) in that very same translation unit (the only one which has access to those definitions).

C++03 had a keyword called export which allowed doing what you are trying to achieve, but it has been removed during standardization of C++11 because it proved to be too difficult to implement for compiler vendors.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM