When the following project is compiled, I get the following compiler error: (Visual Studio 2010)
1>usingclass.obj : error LNK2019: unresolved external symbol "public: static int __cdecl c1::arrSize(int * const)" (??$arrSize#H#c1##SAHQAH#Z) referenced in function "public: void __thiscall usingclass::a(void)" (?a#usingclass##QAEXXZ)
Code:
Headers:
c1.h
#pragma once
#include <array>
class c1
{
c1(void);
~c1(void);
public:
template<class T>
static int arrSize(T arr[]);
};
usingclass.h
#pragma once
#include "c1.h"
class usingclass
{
public:
usingclass(void);
~usingclass(void);
void a();
};
Source files:
c1.cpp
#include "c1.h"
c1::c1(void)
{
}
c1::~c1(void)
{
}
template <class T>
int c1::arrSize(T arr[])
{
return (sizeof(arr)/sizeof(arr[0]));
}
usingclass.cpp
#include "usingclass.h"
usingclass::usingclass(void)
{
}
usingclass::~usingclass(void)
{
}
void usingclass::a()
{
int a[2] = {1,2};
int b = c1::arrSize<int>(a);
}
How do I fix that?
Don't do this! The declaration is misleading.
template <class T>
int c1::arrSize(T arr[])
{
return (sizeof(arr)/sizeof(arr[0]));
}
is equivalent to
template <class T>
int c1::arrSize(T *arr)
{
return (sizeof(arr)/sizeof(arr[0]));
}
which will not give you want you want. The proper way to do it is like this:
class c1
{
c1(void);
~c1(void);
public:
template<class T,int N>
static int arrSize(T (&arr)[N]) { return N; }
};
arrSize takes a reference to an array as a parameter. The type of the array and the size of the array are template parameters and can be deduced by the compiler. The inline member function then just returns the size determined by the compiler.
You need to move
template <class T>
int c1::arrSize(T arr[])
{
return (sizeof(arr)/sizeof(arr[0]));
}
inside c1.h.
Template implementations must be visible to all translation units using that template (unless it's specialized, and in your case it's not).
This solves the compiler error but the underlying issue is solved with Vaughn Cato's answer. I missed that. You'll still need the definition in the header.
I think you have to define your template in c1.h itself. Because when you are including c1.h in your usingclass.h, and try to use template it does not find the expansion for template.
Or If you want to go with implementation of template in c1.cpp, then you have to include c1.cpp as well in usingclass.h.
Related
I'm writing a game engine lib, for the sake of science. I've written static libs successfully in the past, although there were no templated functions.
When dealing with templated functions, I use to sepparate their code from the untemplated ones. Templated functions code lie in the header file, while the others in the .cpp/.hpp file.
Below is a snippet of one of it's modules: signals.
// Connection.h
#pragma once
#include <memory>
#include <functional>
namespace mqs
{
using Disconnector = std::function<void(std::uint32_t)>;
class Connection final
{
public:
explicit Connection(std::shared_ptr<mqs::Disconnector> disconnector, std::uint32_t index);
bool connected() const;
void disconnect() const;
private:
std::uint32_t index;
std::weak_ptr<mqs::Disconnector> disconnector;
};
}
// Signal.h
#pragma once
#include <vector>
#include "connection.hpp"
namespace mqs
{
template <typename...>
class Signal;
template <typename R, typename... A>
class Signal<R(A...)> final
{
public:
Signal();
template <typename Lambda>
mqs::Connection connect(Lambda&& lambda) {
slots.push_back(std::forward<Lambda>(lambda));
return mqs::Connection(disconnector, slots.size() - 1U);
}
void operator()(A&&... args) const;
unsigned connections() const;
private:
std::vector<std::function<R(A...)>> slots;
std::shared_ptr<mqs::Disconnector> disconnector;
};
}
// Connection.hpp
#pragma once
#include "connection.h"
namespace mqs
{
Connection::Connection(std::shared_ptr<mqs::Disconnector> disconnector, std::uint32_t index) {
this->index = index;
this->disconnector = disconnector;
}
bool Connection::connected() const {
return !disconnector.expired();
}
void Connection::disconnect() const {
if (const auto& lock = disconnector.lock()) {
lock->operator()(index);
}
}
}
// Signal.hpp
#pragma once
#include "signal.h"
namespace mqs
{
template <typename R, typename... A>
Signal<R(A...)>::Signal() {
disconnector = std::make_shared<mqs::Disconnector>([this](std::uint32_t index) {
slots.erase(slots.begin() + index);
});
}
template <typename R, typename... A>
void Signal<R(A...)>::operator()(A&&... args) const {
for (auto& slot : slots) {
slot(std::forward<A>(args)...);
}
}
template <typename R, typename... A>
unsigned Signal<R(A...)>::connections() const {
return slots.size();
}
}
It compiles and all, however one of the problems I've been dealing with, is that mqs::Signal (signal.hpp) cannot be included in different headers or it will cause a function already has a body. When including signal.h I get unresolved external symbol which makes sense.
I've also tried making inline all the functions defined in their .hpp files above.
Is there any way to achieve this other than using huge header-only approaches?
As you already figured out, you need to make the function templates inline. This is necessary because the templates first need to be instantiated to become compilable functions, and that means the compiler needs source code.
However, if you look at members like Signal<R(A...)>::disconnector;, you'll notice that they are not dependent on R or A.... Hence, you could move them to a non-template base class.
There's a fairly common convention to use the extension .ipp for implementation files that still need to be included, e.g. because they contain template code. These will typically be included by the corresponding .hpp file, just before the #endif of the header guard. Therefore an .ipp file doesn't need its own header guard.
I'm trying to build a simple heap data structure for practice. When I build the version for double it works fine.
class heapt {
public:
heapt();
heapt(std::initializer_list<double> lst);
void print_heapt();
private:
int size;
int length;
double* elem; //points to root
};
Its constructors works perfectly and the heap is printed as it should. But when I try to generalize it with
template< typename Elem>
as:
template<typename Elem>
class heapt {
public:
heapt();
heapt(std::initializer_list<Elem> lst);
void print_heapt();
private:
int size;
int length;
Elem* elem; //points to root
};
for the class definition and as:
template<typename Elem>
heapt<Elem>::heapt(std::initializer_list<Elem> lst) :
size{ static_cast<int>(lst.size()) },
elem{new Elem[lst.size()]}
{
std::copy(lst.begin(), lst.end(), elem);//Now heaptify elem
build_heapt(elem, lst.size());
}
for one of the constructors used in the main function.
I get two linking errors:
LNK2019 unresolved external symbol "public: void __thiscall heapt::print_heapt(void)" (?print_heapt#?$heapt#H##QAEXXZ) referenced in function _main
LNK2019 unresolved external symbol "public: __thiscall heapt::heapt(class std::initializer_list)" (??0?$heapt#H##QAE#V?$initializer_list#H#std###Z) referenced in function _main
The main function is:
{
heapt<int> hh{ 27,12,3,13,2,4,14,5 };
std::cout << "Hello" << '\n';
hh.print_heapt();
}
EDIT: The heapt class is in the "heap.h" file and the definition for the constructor heapt<Elem>::heapt(std::initializer_list<Elem> lst) is in the "heap.cpp" class, which has #include"heap.h" as a header file. The int main function is in a file named "InSo.cpp", which also has #include"heap.h" as a header file.
In your templated class declaration you are using heapt(std::initializer_list<double> lst); but in your definition you are using std::initializer_list<Elem>. You should change the declaration to heapt(std::initializer_list<Elem> lst);
You are still missing definitions for print_heapt and build_heapt but otherwise it should compile.
EDIT: In light of you clarifying how your source files are set up, see WhozCraig's comment on your initial post. You can either include the definition of the templated class functions in a heap.hpp file, for example, and include it at the end of your heap.h, or just put them all together in one file, e.g.
// heap.h
#ifndef HEAP_H
#define HEAP_H
#include <initializer_list>
template<typename Elem>
class heapt {
public:
heapt();
heapt(std::initializer_list<Elem> lst);
void print_heapt();
private:
int size;
int length;
Elem* elem; //points to root
};
#include "heap.hpp"
#endif
//heap.hpp
#ifndef HEAP_HPP
#define HEAP_HPP
#include "heap.h"
#include <algorithm>
template<typename Elem>
heapt<Elem>::heapt(std::initializer_list<Elem> lst) :
size{ static_cast<int>(lst.size()) },
elem{ new Elem[lst.size()] }
{
std::copy(lst.begin(), lst.end(), elem);//Now heaptify elem
//build_heapt(elem, lst.size());
}
#endif
I've specified a header file like this:
04-Templates_foo.h:
template <typename T>
class foo {
T x, y;
T getX(void);
void setX(T x);
};
And an implementation like this:
04-Templates_foo.cc:
#include "04-Templates_foo.h"
template <typename T>
T foo::getX(void) {
return this->x;
}
void foo::setX(T x) {
this->x = x;
}
My main routine:
04-Templates.cc
#include <iostream>
#include "04-Templates_foo.cc"
int main (void) {
// Do nothing because it doesn't even compile...
}
Compiling this code returns this error:
In file included from 04-Templates.cc:2:
./04-Templates_foo.cc:4:3: error: expected a class or namespace
T foo::getX(void) {
^
1 error generated.
I can't imagine what the problem is. Why can't I specify the function foo::getX? It's a class name, although the compiler said it is expecting a class and didn't find one :-/
If it may be important. I'm compiling this on a MacBook Pro Retina Mid 2012 with Mavericks.
I used this compile-command:
g++ -o 04-Templates 04-Templates.cc
Suggestions for a better title are welcome ;)
In the definition of foo::getX (and setX as well), what kind of foo?
Because it's a template class, you have to specify that, like
template<typename T>
T foo<T>::getX(void) { ... }
You also have to tell the compiler that member functions are templates for each function in a templated class. So you have to do it for setX as well:
template<typename T>
void foo<T>::setX(T x) { ... }
While instantiating the class in main I am getting these two errors:
error LNK2019: unresolved external symbol "public: __thiscall templateClass<int>::templateClass<int>(void)" (??0?$templateClass#H##QAE#XZ) referenced in function _wmain
error LNK2019: unresolved external symbol "public: __thiscall templateClass<int>::~templateClass<int>(void)" (??1?$templateClass#H##QAE#XZ) referenced in function _wmain
Code
templateClass.h
#pragma once
#define MAX 10
template<class T>
class templateClass
{
private:
T arr[MAX];
int size;
public:
templateClass(void);
void initArrayWithZero();
void pushElementIntoArray(T element );
void printArray();
~templateClass(void);
};
templateclass.c
#include "templateClass.h"
template <class T>
templateClass<T>::templateClass(void)
{
size = 0;
}
template <class T>
templateClass<T>::~templateClass(void)
{
}
template <class T>
void templateClass<T>::pushElementIntoArray(T element )
{
if(size<MAX)
{
T[size++] = element;
}
else
{
//Give error code
}
}
template <class T>
void templateClass<T>::initArrayWithZero()
{
for(i= 0; i<MAX; i++)
T[i] = 0;
}
template <class T>
void templateClass<T>::printArray()
{
for( int i =0; i<MAX; i++)
cout<<"\n"T[i];
}
Main.cpp
#include "stdafx.h"
#include "templateClass.h"
int _tmain(int argc, _TCHAR* argv[])
{
templateClass<int> intarray;
return 0;
}
How can I fix this?
Template classes such as templateClass<T> must (usually) be implemented in header (.h) files.
As such, you need to move the content of templateclass.c to templateClass.h and delete templateclass.c.
See Why can templates only be implemented in the header file? for more information.
I've written this beauty:
#include <iostream>
struct something {
static const char ref[];
};
const char something::ref[] = "";
template<int N, const char(&t_ref)[N], typename to> struct to_literal {
private:
static to hidden[N];
public:
to_literal()
: ref(hidden) {
for(int i = 0; i < N; i++)
hidden[i] = t_ref[i];
}
const to(&ref)[N];
};
template<int N, const char(&ref)[N], typename to> const to* make_literal() {
return to_literal<N, ref, to>().ref;
}
int main() {
const wchar_t* lit = make_literal<sizeof(something::ref), something::ref, wchar_t>();
}
It somewhat cleanly converts between string literal types. But when I compile it, MSVC says that the make_literal function is an undefined external function- which is clearly untrue as it's defined right there.
Edit: I've managed to reduce the problem down without all of the template gunk.
struct some {
friend int main();
private:
static wchar_t hidden[40];
public:
some()
{
}
};
int main() {
std::cout << some::hidden;
//const wchar_t* lit = make_literal<sizeof(something::ref), something::ref, wchar_t>();
}
main.obj : error LNK2001: unresolved external symbol "private: static wchar_t * some::hidden" (?hidden#some##0PA_WA)
It's just a static array. Does life hate me?
The issue is that is that to_literal::hidden is declared but never defined. Take another look:
struct something {
static const char ref[]; // declaration of something::ref
};
const char something::ref[] = ""; // definition of something::ref
template<int N, const char(&t_ref)[N], typename to> struct to_literal {
private:
static to hidden[N]; // declaration of to_literal::hidden (but there's no
// definition anywhere)
public:
to_literal()
: ref(hidden) {
for(int i = 0; i < N; i++)
hidden[i] = t_ref[i];
}
const to(&ref)[N];
};
To fix this, add a proper definition of to_literal::hidden:
template<int N, const char(&t_ref)[N], typename to>
to to_literal<N, t_ref, to>::hidden[N]; // definition of to_literal::hidden
When you define static members, a declaration does not suffice. You must provide a definition outside the class. I.e. add
wchar_t some::hidden[40];
outside the class, and it'll be defined.
Otherwise, if C++ allowed this, it'd cause the same problem as defining a global variable in a header -- every .cpp file that includes it will come with a duplicate definition, and at link time you'd get a multiply-defined symbol error.
You're declaring but not defining the static member. Add something like...
template<int N, const char(&t_ref)[N], typename to>
to to_literal<N, t_ref, to>::hidden[N];
I tried to check in MSVC for you too, but with VS2005 I get another stupid error...
template<int N, const char(&t_ref)[N], typename to>
to to_literal<N, t_ref, to>::hidden[N];
...compiler complains of...
error C3860: template argument list following class template name must list parameters in the order used in template parameter list
Looks like when they fix one bug, there's another one behind it ;-/.
When I built this with VC 2008, that wasn't the error I got. The error was:
Error 1 error LNK2001: unresolved
external symbol "private: static
wchar_t * to_literal<1,&public: static
char const * const
something::ref,wchar_t>::hidden"
(?hidden#?$to_literal#$00$1?ref#something##2QBDB_W##0PA_WA) main.obj Enabler
Removing static from the to hidden[N]; member resolved the issue.
Are you sure you got the error message correct?