Why separating template definition/declaration sometimes work - c++

I'm wide awake at 1AM trying to figure out a compilation error I'm having.
I can't really write the exact code but I'll do my best to make my question clear.
ClassWithTemplateFunction.hpp
#include "StructA.hpp"
#include "ClassB.hpp"
class ClassWithTemplateFunction
{
template<typename A>
void MyTemplateFunc();
}
ClassWithTemplateFunction.cpp
template<typename T>
void ClassWithTemplateFunction::MyTemplateFunc()
{
// code block
}
StructA.hpp
struct ClassWithTemplateFunction;
struct StructA
{
void StructAFunc(ClassWithTemplateFunction* templ);
}
StructA.cpp
#include "ClassWithTemplateFunction.hpp"
#include "StructA.hpp"
StructA::StructAFunc(ClassWithTemplateFunction* templ)
{
templ->MyTemplateFunc<SomeTemplate>();
}
The above codes work great. "SomeTemplate" is also another class. But then I added a new class which also uses the template function:
ClassB.hpp
class ClassWithTemplateFunction;
class ClassB
{
void ClassBFunc();
}
ClassB.cpp
#include "ClassB.hpp"
#include "ClassWithTemplateFunction.hpp"
void ClassB::ClassBFunc(ClassWithTemplateFunction* templ)
{
templ->MyTemplateFunc<SomeTemplate>();
}
And for some reason, this now introduced a linker error (undefined reference to MyTemplateFunc()). I can't figure out why it suddenly produced this issue. Obviously it can be fixed by moving the definition to the header file, but I want to understand, before adding ClassB, the code works just fine.

If ClassWithTemplateFunction.cpp instantiates MyTemplateFunc with some template arguments somehow (e.g. by calling it), you'll be able to use MyTemplateFunc with the exact same arguments anywhere in the program.
That's why you don't get an undefined reference in the first case.
But it's not possible for an invocation of MyTemplateFunc with the exact same template arguments in a different file to cause an undefined reference. Please check if the template argument is really the same in both cases.

Related

Forward declaration is not working as expected

I'm defining a class SpatialCriterionCallback in a header file "spatialcriterion.h" like this:
#include "ros/ros.h"
#include "neuromorphic_stereo/MatchingCandidates.h"
#include <vector>
class SpatialCriterionCallback
{
public:
// constructors & destructors
SpatialCriterionCallback()=default;
SpatialCriterionCallback(ros::NodeHandle);
~SpatialCriterionCallback()=default;
private:
std::vector<neuromorphic_stereo::MatchingCandidates> matching_candidates;
void subscriberCallbackFunction(constneuromorphic_stereo::MatchingCandidates&);
}
Then in the file "spatialcriterion.cpp" I'm defining a constructor that invokes a ros::SubscriberNode like this:
#include "spatialcriterioncallback.h"
SpatialCriterionCallback::SpatialCriterionCallback(ros::NodeHandle n)
{
this->n =n;
this->time_criterion_topic_handle = this->n.subscribe("TimeCriterionTopic",
1e4,
&SpatialCriterionCallback::subscriberCallbackFunction,
this);
}
When I try compiling this within a qtcreator project, the compiler tells me
error: undefined reference to
`SpatialCriterionCallback::subscriberCallbackFunction(neuromorphic_stereo::MatchingCandidates_ const&)'
When I add the following lines to my "spatialcriterion.cpp" file it will compile just fine:
void SpatialCriterionCallback::subscriberCallbackFunction(const neuromorphic_stereo::MatchingCandidates & msg){
this->matching_candidates.push_back(msg);
}
Now my question is: Shouldn't this code compile without the function definition, because the function subscriberCallbackFunction() has already been declared in "spatialcriterion.h"? Why is it necessary for the compiler to have the function defined?
I also tried finding an explanation to this behaviour here, but all the other posts about failing forward declaration (like this or this) aren't exactly what I'm looking for.
Even if you declared the method in your class, it doesn't exist.
When you reference that function in the constructor, the linker tells you that it needs to know where the method is.
The code compiles fine, it doesn't link.

function is not found when instantiate a template function in VS2017

It might be a stupid question but I totally don't have any idea about the prompt.
The code is in a .cpp file
template <typename T> void foo2(T){}
template void foo2<int>(int);
VS2017 keeps telling me that: Function definition for function 'foo2' is not found. However, the code actually works in VS2017, no error message if I run it. I don't know if it is an IDE-specific problem or it is the code problem. As it is quite annoying, does anyone know why the prompt appears and how to fix it? Thanks!
=========Update===========
Here is the full code(Move the instantiation to .h file but still have the same problem):
test.h
#pragma once
template <typename T> void foo2(T);
template void foo2<int>(int);
test.cpp
#include "test.h"
#include "stdafx.h"
template <typename T>void foo2(T){}
main file
#include "stdafx.h"
#include "test.h"
int main()
{
int a = 1;
foo2(a);
}
I tend to believe that an IDE-specific question. If I ask VS to show the potential fix, it will create the following code in .cpp file:
template void foo2(int)
{
return template void();
}
which is definitely wrong. Even cannot pass the compilation.
With the expanded code, I get the idea.
You actually want extern template void foo2<int>(int); in the .h file. There should be only one instantiation, in test.cpp. extern template is new in C++11, so while your book might not yet cover it it's certainly understood by VS2017.

Issue with extern templates c++

I'm using C++ and I'm having struggle with extern templates. In opposite to C# the whole template implementation is really nasty in C++ :(
template_test.hpp
template<class T>
class CFoo {
public:
T Foo_Func(const T& test);
};
template_test.cpp
#include "Template_Test.hpp"
template<class T>
T CFoo<T>::Foo_Func(const T& test)
{
return test;
}
template_test2.hpp
#include "Template_Test.hpp"
extern template class CFoo<int>;
int Template_Tests();
template_test2.cpp
#include "Template_Test2.hpp"
int Template_Tests()
{
CFoo<int> foo_instance;
//this causes an undefined reference
int res = foo_instance.Foo_Func(1);
return res;
}
why does the linker not find my function. I thought extern templates worked the same why as extern variables.
(Put extern int test; in the header file and int test = 0 in the source file.)
thanks for your support:)
Solution 1
One way to solve this issue is to implements the template class's function without function's definitions. in this case:
template<class T>
class CFoo {
public:
T Foo_Func(const T& test) {
return test;
}
};
And then, you don't even need the extern part. I aware that your programmer sense keep telling you to avoid from this, and always to separate between your class functions' definitions, and their implementation- but in template case in c++, it's the easiest solution for this language's huge problem.
An important thing that you need to know- there is a big different between the solutions for this issue between differnt IDEs, but this easy solution works in most of them (if not always).
Solution 2
Another option, if you still want to separate the implementations from the definitions you can include the .cpp file, as well as the .hpp/.h file:
template_test2.hpp
#include "Template_Test.hpp"
#include "Template_Test.cpp"
/*extern template class CFoo<int>;*/ // Again, you don't need this extern
int Template_Tests();
Solution 3
It is the closest way to the way that you tried. in the end of template_test.cpp file, add the following line:
template class CFoo<int>;
and remove the line extern template class CFoo<int>; from the template_test2.hpp file.
I hope that you will find it helping, Korel.

C++ - "Unspecialised class template" error with shared_ptr

I have a class Room and it holds a vector of shared_ptrs to Option objects like so:
private:
vector<shared_ptr<Option> > options;
But for some reason when I build, I get the following errors:
'shared_ptr' : unspecialized class template can't be used as a template argument for template parameter '_Ty', expected a real type
'std::tr1::shared_ptr' : use of class template requires template argument list
Strangely, I also have a vector of shared_ptrs, exact same syntax but there's no problem with that one.
There's also a bunch of places that bring up the error "'Option': undeclared identifier", which brings me to think it might be a problem with the Option class, but it seems to be fine. Here's the code for Option:
Option.h:
#pragma once
#include "Room.h"
#include <memory>
using namespace std;
class Option
{
protected:
int id;
char* text;
public:
Option(void);
Option(int, char*);
virtual ~Option(void);
char* getText();
int getID();
};
Option.cpp:
#include "Option.h"
#include "Room.h"
#include <memory>
using namespace std;
Option::Option(void)
{
}
Option::Option(int newID, char* newText){
id = newID;
text = newText;
}
Option::~Option(void)
{
}
char* Option::getText(){
return text;
}
int Option::getID(){
return id;
}
There is a bit of conjecture in this answer since you haven't posted the code for the Room class. I'm assuming this code
private:
vector<shared_ptr<Option> > options;
is in Room.h. Your Option.h file includes Room.h, hence the Room class gets declared before the Option class. So Option is an incomplete type when the Room class' destructor is compiled and the shared_ptr implementation tries to delete the Option object.
From the code above, I don't see why Option.h needs to include Room.h, in fact, it should be the other way around. If it does indeed need to include the file, you should be able to work around the problem by explicitly declaring Room::~Room() out-of-line in Room.cpp.
EDIT:
Turns out ~shared_ptr<T> does not require T to be a complete type. However, shared_ptr<T>( T* ) and shared_ptr<T>::reset( T* ) do, and the problem may be because some operation on the vector is invoking a call to one of these (more likely the former).
vector<shared_ptr<Option >>
You almost did that right :)
vector<shared_ptr<Option> >
It's the two > characters that, when touching, cause the strange errors you see. It is being interpreted as the >> operator.
BTW, thank you for posting your code exactly as it is rather than typing it back in and possibly hiding the mistake.

"Undefined symbols" linker error with simple template class

Been away from C++ for a few years and am getting a linker error from the following code:
Gene.h
#ifndef GENE_H_INCLUDED
#define GENE_H_INCLUDED
template <typename T>
class Gene {
public:
T getValue();
void setValue(T value);
void setRange(T min, T max);
private:
T value;
T minValue;
T maxValue;
};
#endif // GENE_H_INCLUDED
Gene.cpp
#include "Gene.h"
template <typename T>
T Gene<T>::getValue() {
return this->value;
}
template <typename T>
void Gene<T>::setValue(T value) {
if(value >= this->minValue && value <= this->minValue) {
this->value = value;
}
}
template <typename T>
void Gene<T>::setRange(T min, T max) {
this->minValue = min;
this->maxValue = max;
}
Using Code::Blocks and GCC if it matters to anyone. Also, clearly porting some GA stuff to C++ for fun and practice.
The template definition (the cpp file in your code) has to be included prior to instantiating a given template class, so you either have to include function definitions in the header, or #include the cpp file prior to using the class (or do explicit instantiations if you have a limited number of them).
Including the cpp file containing the implementations of the template class functions works. However, IMHO, this is weird and awkward. There must surely be a slicker way of doing this?
If you have only a few different instances to create, and know them beforehand, then you can use "explicit instantiation"
This works something like this:
At the top of gene.cpp add the following lines
template class Gene<int>;
template class Gene<float>;
In if(value >= this->minValue && value <= this->minValue) the second minValue should be maxValue, no?
Echo what Sean said: What's the error message? You've defined and declared the functions, but you've not used them in anything anywhere, nor do I see an error (besides the typo).
TLDR
It seems that you need an Explicit Instantiation i.e. to actually create the class. Since template classes are just "instructions" on how to create a class you actually need to tell the compiler to create the class. Otherwise the linker won't find anything when it goes looking.
The thorough explanation
When compiling your code g++ goes through a number of steps the problem you're seeing occurs in the Linking step. Template classes define how classes "should" be created, they're literally templates. During compile time g++ compiles each cpp file individually so the compiler sees your template on how to create a class but no instructions on what "classes" to create. Therefore ignores it. Later during the linking step the g++ attempts to link the file containing the class (the one that doesn't exist) and fails to find it ultimately returning an error.
To remedy this you actually need to "explicitly instantiate" the class by adding the following lines to Gene.cpp after the definition of the class
template class Gene<whatever_type_u_wanna_use_t>;int
Check out these docs I found them to be super helpful.