ChatGPT解决这个技术问题 Extra ChatGPT

Storing C++ template function definitions in a .CPP file

I have some template code that I would prefer to have stored in a CPP file instead of inline in the header. I know this can be done as long as you know which template types will be used. For example:

.h file

class foo
{
public:
    template <typename T>
    void do(const T& t);
};

.cpp file

template <typename T>
void foo::do(const T& t)
{
    // Do something with t
}

template void foo::do<int>(const int&);
template void foo::do<std::string>(const std::string&);

Note the last two lines - the foo::do template function is only used with ints and std::strings, so those definitions mean the app will link.

My question is - is this a nasty hack or will this work with other compilers/linkers? I am only using this code with VS2008 at the moment but will be wanting to port to other environments.

I had no idea this was possible - an interesting trick! It would have helped some recent tasks considerable to know this - cheers!
The thing that stomps me is the usage of do as an identifier :p
i have done somerhing similar with gcc, but still researching
This is not a "hack", it is forward decleration. This has a place in standard of the language; so yes, it is allowed in every standard conformant compiler.
What if you have dozens of methods? Can you just do template class foo<int>;template class foo<std::string>; at the end of the .cpp file?

Q
Qix - MONICA WAS MISTREATED

The problem you describe can be solved by defining the template in the header, or via the approach you describe above.

I recommend reading the following points from the C++ FAQ Lite:

Why can’t I separate the definition of my templates class from its declaration and put it inside a .cpp file?

How can I avoid linker errors with my template functions?

How does the C++ keyword export help with template linker errors?

They go into a lot of detail about these (and other) template issues.


Just to complement the answer, the referenced link answers the question positively, i.e. it is possible to do what Rob suggested and have the code to be portable.
Can you just post the relevant parts in the answer itself? Why is such referencing even allowed on SO. I have no clue what to look for in this link as it has been heavily changed since.
Q
Qix - MONICA WAS MISTREATED

For others on this page wondering what the correct syntax is (as did I) for explicit template specialisation (or at least in VS2008), its the following...

In your .h file...

template<typename T>
class foo
{
public:
    void bar(const T &t);
};

And in your .cpp file

template <class T>
void foo<T>::bar(const T &t)
{ }

// Explicit template instantiation
template class foo<int>;

Do you mean "for explicit CLASS template specialiastion". In that case will that cover every function that the templated class has ?
@Arthur seems not, I have some template methods stay in header and most of other methods in cpp, works fine. Very nice solution.
In the asker's case, they have a function template, not a class template.
So, you can put multiple template class foo<...> at the bottom of certain file, right? So, one file for definitions for int, for example, Other for float, if there are any differences, if there are not differences you can just pul template class foo under int? Am I getting this right?
I'm completely completely confused about your use of typename AND class, here..
K
Konrad Rudolph

This code is well-formed. You only have to pay attention that the definition of the template is visible at the point of instantiation. To quote the standard, § 14.7.2.4:

The definition of a non-exported function template, a non-exported member function template, or a non-exported member function or static data member of a class template shall be present in every translation unit in which it is explicitly instantiated.


What does non-exported mean?
@Dan Visible only inside its compilation unit, not outside it. If you link multiple compilation units together, exported symbols can be used across them (and must have a single, or at least, in the case of templates, consistent definitions, otherwise you run into UB).
Thanks. I thought that all functions are (by default) visible outside the compilation unit. If I have two compilation units a.cpp (defining the function a() {}) and b.cpp (defining the function b() { a() }), then this will successfully link. If I'm right, then the above quote would seem not to apply for the typical case... am I going wrong somewhere?
@Dan Trivial counterexample: inline functions
@Dan Function templates are implicitly inline. The reason being that without a standardised C++ ABI it’s hard/impossible to define the effect that this would otherwise have.
C
Cameron Tacklind

Your example is correct but not very portable. There is also a slightly cleaner syntax that can be used (as pointed out by @namespace-sid, among others).

However, suppose the templated class is part of some library that is to be shared...

Should other versions of the templated class be compiled?

Is the library maintainer supposed to anticipate all possible templated uses of the class?

An Alternate Approach

Add a third file that is the template implementation/instantiation file in your sources.

lib/foo.hpp in/from library

#pragma once

template <typename T>
class foo
{
public:
    void bar(const T&);
};

lib/foo.cpp compiling this file directly just wastes compilation time

// Include guard here, just in case
#pragma once

#include "foo.hpp"

template <typename T>
void foo::bar(const T& arg)
{
    // Do something with `arg`
}

foo.MyType.cpp using the library, explicit template instantiation of foo<MyType>

// Consider adding "anti-guard" to make sure it's not included in other translation units
#if __INCLUDE_LEVEL__
  #error "Don't include this file"
#endif

// Yes, we include the .cpp file
#include <lib/foo.cpp>
#include "MyType.hpp"

template class foo<MyType>;

Of course, you can have multiple implementations in the third file. Or you might want multiple implementation files, one for each type (or set of types) you'd like to use, for instance.

This setup should reduce compile times, especially for heavily used complicated templated code, because you're not recompiling the same header file in each translation unit. It also enables better detection of which code needs to be recompiled, by compilers and build scripts, reducing incremental build burden.

Usage Examples

foo.MyType.hpp needs to know about foo<MyType>'s public interface but not .cpp sources

#pragma once

#include <lib/foo.hpp>
#include "MyType.hpp"

// Declare `temp`. Doesn't need to include `foo.cpp`
extern foo<MyType> temp;

examples.cpp can reference local declaration but also doesn't recompile foo<MyType>

#include "foo.MyType.hpp"

MyType instance;

// Define `temp`. Doesn't need to include `foo.cpp`
foo<MyType> temp;

void example_1() {
    // Use `temp`
    temp.bar(instance);
}

void example_2() {
    // Function local instance
    foo<MyType> temp2;

    // Use templated library function
    temp2.bar(instance);
}

error.cpp example that would work with pure header templates but doesn't here

#include <lib/foo.hpp>

// Causes compilation errors at link time since we never had the explicit instantiation:
// template class foo<int>;
// GCC linker gives an error: "undefined reference to `foo<int>::bar()'"
foo<int> nonExplicitlyInstantiatedTemplate;
void linkerError()
{
    nonExplicitlyInstantiatedTemplate.bar();
}

Note that most compilers/linters/code helpers won't detect this as an error, since there is no error according to C++ standard. But when you go to link this translation unit into a complete executable, the linker won't find a defined version of foo<int>.

If memory serves, I originally got the idea for this from SO. But when I wrote this answer, I could not for the life of me find that original SOA. Today, I think I found it: https://stackoverflow.com/a/495056/4612476


Separation of implementation details (aka definitions in foo.cpp) from which versions are actually compiled (in foo-impl.cpp) and declarations (in foo.h). I dislike that most C++ templates are defined entirely in header files. That is counter to the C/C++ standard of pairs of c[pp]/h for each class/namespace/whatever grouping you use. People seem to still use monolithic header files simply because this alternative is not widely used or known.
@MK. I was putting the explicit template instantiations at the end of the definition in the source file at first until I needed further instantiations elsewhere (e.g. unit tests using a mock as the templated type). This separation allows me to add more instantiations externally. Furthermore, it still works when I keep the original as a h/cpp pair although I had to surround the original list of instantiations in an include guard, but I could still compile the foo.cpp as normal. I am still quite new to C++ though and would be interested to know if this mixed usage has any additional caveat.
I think it is preferable to decouple foo.cpp and foo-impl.cpp. Do not #include "foo.cpp" in the foo-impl.cpp file; instead, add the declaration extern template class foo<int>; to foo.cpp to prevent the compiler from instantiating the template when compiling foo.cpp. Ensure that the build system builds both .cpp files and passes both of the object files to the linker. This has multiple benefits: a) it's clear in foo.cpp that there is no instantiation; b) changes to foo.cpp do not require a recompilation of foo-impl.cpp.
This is a very good approach to the problem of templates definitions that takes best of both worlds -- header implementation and instantiation for frequently used types. The only change I would make to this setup is to rename foo.cpp into foo_impl.h and foo-impl.cpp into just foo.cpp. I also would add typedefs for instantiations from foo.cpp to foo.h, likewise using foo_int = foo<int>;. The trick is to provide users two header interfaces for a choice. When user needs pre-defined instantiation he includes foo.h, when user needs something out of order he includes foo_impl.h.
Shouldn't lib/foo.cpp be lib/foo.inl so project-generating tools like cmake know it shouldn't be compiled directly?
m
moonshadow

This should work fine everywhere templates are supported. Explicit template instantiation is part of the C++ standard.


C
Cássio Renan

That is a standard way to define template functions. I think there are three methods I read for defining templates. Or probably 4. Each with pros and cons.

Define in class definition. I don't like this at all because I think class definitions are strictly for reference and should be easy to read. However it is much less tricky to define templates in class than outside. And not all template declarations are on the same level of complexity. This method also makes the template a true template. Define the template in the same header, but outside of the class. This is my preferred way most of the times. It keeps your class definition tidy, the template remains a true template. It however requires full template naming which can be tricky. Also, your code is available to all. But if you need your code to be inline this is the only way. You can also accomplish this by creating a .INL file at the end of your class definitions. Include the header.h and implementation.CPP into your main.CPP. I think that's how its done. You won't have to prepare any pre instantiations, it will behave like a true template. The problem I have with it is that it is not natural. We don't normally include and expect to include source files. I guess since you included the source file, the template functions can be inlined. This last method, which was the posted way, is defining the templates in a source file, just like number 3; but instead of including the source file, we pre instantiate the templates to ones we will need. I have no problem with this method and it comes in handy sometimes. We have one big code, it cannot benefit from being inlined so just put it in a CPP file. And if we know common instantiations and we can predefine them. This saves us from writing basically the same thing 5, 10 times. This method has the benefit of keeping our code proprietary. But I don't recommend putting tiny, regularly used functions in CPP files. As this will reduce the performance of your library.

Note, I am not aware of the consequences of a bloated obj file.


R
Red XIII

This is definitely not a nasty hack, but be aware of the fact that you will have to do it (the explicit template specialization) for every class/type you want to use with the given template. In case of MANY types requesting template instantiation there can be A LOT of lines in your .cpp file. To remedy this problem you can have a TemplateClassInst.cpp in every project you use so that you have greater control what types will be instantiated. Obviously this solution will not be perfect (aka silver bullet) as you might end up breaking the ODR :).


Are you certain it will break the ODR? If the instantiation lines in TemplateClassInst.cpp refer to the identical source file (containing the template function definitions), isn't that guaranteed not to violate the ODR since all definitions are identical (even if repeated)?
Please, what is ODR?
T
TarmoPikaro

Let's take one example, let's say for some reason you want to have a template class:

//test_template.h:
#pragma once
#include <cstdio>

template <class T>
class DemoT
{
public:
    void test()
    {
        printf("ok\n");
    }
};

template <>
void DemoT<int>::test()
{
    printf("int test (int)\n");
}


template <>
void DemoT<bool>::test()
{
    printf("int test (bool)\n");
}

If you compile this code with Visual Studio - it works out of box. gcc will produce linker error (if same header file is used from multiple .cpp files):

error : multiple definition of `DemoT<int>::test()'; your.o: .../test_template.h:16: first defined here

It's possible to move implementation to .cpp file, but then you need to declare class like this -

//test_template.h:
#pragma once
#include <cstdio>

template <class T>
class DemoT
{
public:
    void test()
    {
        printf("ok\n");
    }
};

template <>
void DemoT<int>::test();

template <>
void DemoT<bool>::test();

// Instantiate parametrized template classes, implementation resides on .cpp side.
template class DemoT<bool>;
template class DemoT<int>;

And then .cpp will look like this:

//test_template.cpp:
#include "test_template.h"

template <>
void DemoT<int>::test()
{
    printf("int test (int)\n");
}


template <>
void DemoT<bool>::test()
{
    printf("int test (bool)\n");
}

Without two last lines in header file - gcc will work fine, but Visual studio will produce an error:

 error LNK2019: unresolved external symbol "public: void __cdecl DemoT<int>::test(void)" (?test@?$DemoT@H@@QEAAXXZ) referenced in function

template class syntax is optional in case if you want to expose function via .dll export, but this is applicable only for windows platform - so test_template.h could look like this:

//test_template.h:
#pragma once
#include <cstdio>

template <class T>
class DemoT
{
public:
    void test()
    {
        printf("ok\n");
    }
};

#ifdef _WIN32
    #define DLL_EXPORT __declspec(dllexport) 
#else
    #define DLL_EXPORT
#endif

template <>
void DLL_EXPORT DemoT<int>::test();

template <>
void DLL_EXPORT DemoT<bool>::test();

with .cpp file from previous example.

This however gives more headache to linker, so it's recommended to use previous example if you don't export .dll function.


Excellent answer
B
Ben Collins

There is, in the latest standard, a keyword (export) that would help alleviate this issue, but it isn't implemented in any compiler that I'm aware of, other than Comeau.

See the FAQ-lite about this.


AFAIK, export is dead because they are facing newer and newer issues, each time they resolve the last, making the overall solution more and more complicated. And the "export" keyword won't enable you to "export" from a CPP anyway (still from H. Sutter's anyway). So I say: Don't hold your breath...
To implement export the compiler still requires the full template definition. All you gain is having it in a sort-of-compiled form. But really there's no point to it.
...and it's gone from the standard, due to excessive complication for minimal gain.
K
KronuZ

None of above worked for me, so here is how y solved it, my class have only 1 method templated..

.h

class Model
{
    template <class T>
    void build(T* b, uint32_t number);
};

.cpp

#include "Model.h"
template <class T>
void Model::build(T* b, uint32_t number)
{
    //implementation
}

void TemporaryFunction()
{
    Model m;
    m.build<B1>(new B1(),1);
    m.build<B2>(new B2(), 1);
    m.build<B3>(new B3(), 1);
}

this avoid linker errors, and no need to call TemporaryFunction at all


Your answer same as the question and it does not work!
Down voted, but this code doesn't compile and work when you actually try and include the header file in another source file to start using this templated class. You will get unresolved symbol linker errors.
L
Lou Franco

Yes, that's the standard way to do specializiation explicit instantiation. As you stated, you cannot instantiate this template with other types.

Edit: corrected based on comment.


Being picky about terminology it's an "explicit instantiation".
D
Didii

Time for an update! Create an inline (.inl, or probably any other) file and simply copy all your definitions in it. Be sure to add the template above each function (template <typename T, ...>). Now instead of including the header file in the inline file you do the opposite. Include the inline file after the declaration of your class (#include "file.inl").

I don't really know why no one has mentioned this. I see no immediate drawbacks.


The immediate drawbacks is it is fundamentally the same as just defining the template functions directly in the header. Once you #include "file.inl", the preprocessor is going to paste the contents of file.inl directly into the header. Whatever reason you wanted to avoid the implementation going in the header, this solution doesn't solve that problem.
- and means you're, technically unnecessarily, burdening yourself w/ the task of writing all the verbose, mind-bending boilerplate needed by out-of-line template definitions. I get why people want to do it - to achieve the most parity with non-template declarations/definitions, to keep the interface declaration looking tidy, etc. - but it's not always worth the hassle. It's a case of evaluating the trade-offs on both sides and picking the least bad. ... until namespace class becomes a thing :O [please be a thing]
@Andrew It seems to have gotten stuck in the Committee's pipes, although I think I saw someone saying that wasn't intentional. I wish it had made it into C++17. Maybe next decade.
@CodyGray: Technically, this is indeed the same for the compiler and it is therefore not reducing the compile time. Still I think this is worth mentioning and practiced in a number of projects I have seen. Going down this road helps to separate Interface from the definition, which is a good practice. In this case it doesn't help with ABI compatibility or the like, but it eases reading and understanding the Interface.
B
Benoît

There is nothing wrong with the example you have given. But i must say i believe it's not efficient to store function definitions in a cpp file. I only understand the need to separate the function's declaration and definition.

When used together with explicit class instantiation, the Boost Concept Check Library (BCCL) can help you generate template function code in cpp files.


What is inefficient about it?

关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now