I am getting an unresolved external compile error when compiling my template classes. I have separated the code into .h and .cpp files.
I read some posts and now I understand that this will not work due to linking issues as explained in this post.
include the full definition of the member function in the template's header file and not have a source file for the template,
define all the member functions in the template's source file as "inline", or
define the member functions in the template's source with the "export" keyword. Unfortunately this isn't supported by a lot of compilers.
However, none of these options will work since I have conversion functions between a number of these template functions (which results in a cross include compile issue).
How do I fix this issue?
EDIT: Include Code
StaticArray.h
template<typename T>
class Array;
template<typename T, unsigned int N>
class StaticArray
{
protected:
T* _data[N];
public:
StaticArray();
StaticArray(const StaticArray<T,N>& other);
~StaticArray();
void Release(unsigned int index);
void Release(T* data);
void ReleaseAll();
Array<T> ToArray();
bool operator == (const StaticArray<T,N>& other);
bool operator != (const StaticArray<T,N>& other);
T*& operator [] (unsigned int index) const;
StaticArray<T,N>& operator = (const StaticArray<T,N>& other);
};
StaticArray.cpp
#pragma region StaticArray::CoreMethods
template<typename T, unsigned int N>
StaticArray<T, N>::StaticArray()
{
for (unsigned int i = 0; i < N; i++)
{
this->_data[i] = null;
}
}
template<typename T, unsigned int N>
StaticArray<T, N>::StaticArray(const StaticArray<T,N>& other)
{
for (unsigned int i = 0; i < N; i++)
{
this->_data[i] = other._data[i];
}
}
template<typename T, unsigned int N>
StaticArray<T, N>::~StaticArray()
{
}
#pragma endregion
#pragma region StaticArray::Methods
template<typename T, unsigned int N>
void StaticArray<T,N>::Release(unsigned int index)
{
if (index < N)
{
delete this->_data[i];
this->_data[i] = null;
}
else
{
throw new Exception::IndexOutOfBoundsException("StaticArray accessed at index greater than N.");
}
}
template<typename T, unsigned int N>
void StaticArray<T,N>::Release(T* data)
{
if (data == null)
{
throw new Exception::InvalidArgumentException("StaticArray Release call argument must not be null.");
}
for (unsigned int i = 0; i < N; i++)
{
if (this->_data[i] == data)
{
this->Release(i);
return;
}
}
throw new Exception::InvalidArgumentException("StaticArray Release call argument was not in the array.");
}
template<typename T, unsigned int N>
void StaticArray<T,N>::ReleaseAll()
{
for (unsigned int i = 0; i < N; i++)
{
if (this->_data[i] != null)
{
delete this->_data[i];
this->_data[i] = null;
}
}
}
template<typename T, unsigned int N>
Array<T> StaticArray<T,N>::ToArray()
{
Array<T> ret(N);
for (unsigned int i = 0; i < N; i++)
{
ret[i] = this->_data[i];
}
return ret;
}
#pragma endregion
#pragma region StaticArray::OperatorOverloads
template<typename T, unsigned int N>
bool StaticArray<T,N>::operator == (const StaticArray<T,N>& other)
{
for (unsigned int i = 0; i < N; i++)
{
if (this->_data[i] != other._data[i])
{
return false;
}
}
return true;
}
template<typename T, unsigned int N>
bool StaticArray<T,N>::operator != (const StaticArray<T,N>& other)
{
return !((*this) == other);
}
template<typename T, unsigned int N>
T*& StaticArray<T, N>::operator[](unsigned int index) const
{
if (index < N)
{
return this->_data[index];
}
else
{
throw new Exception::IndexOutOfBoundsException("StaticArray accessed at index greater than N.");
}
}
template<typename T, unsigned int N>
StaticArray<T, N>& StaticArray<T, N>::operator = (const StaticArray<T,N>& other)
{
for (unsigned int i = 0; i < N; i++)
{
this->_data[i] = other._data[i];
}
return *this;
}
main.cpp
#include "StaticArray.h"
#include "Array.h"
int main(int argc, char** argv[])
{
StaticArray<int,5> sar;
sar[0] = new int(1);
sar[1] = new int(2);
sar[2] = new int(3);
sar[3] = new int(4);
sar[4] = new int(5);
return 0;
}
You don't have to have a definition of class Array to use class StaticArray except at the point you actually call ToArray. So it doesn't matter in which order the templates and their functions are declared, just as long as each one forward-declares the classes it uses.
Explicit template instantiation. Add this line to the bottom of StaticArray.cpp:
template class StaticArray<int,5>;
I see the forward declaration for class Array at the beginning of StaticArray.h. In your main.cpp try swapping the lines to
#include "Array.h"
#include "StaticArray.h"
from a quick glance it seems like the compiler hasnt seen Array when its building StaticArray and hence unresolved external?
I eventually managed to figure out the best way to make this functionality work in a structured manner.
You need to remove the conversions (such as StaticArray to Array) from the StaticArray class and instead have both StaticArray and Array included in another code file that contains utility functions (such as conversions).
The key here is to keep the includes working upwards. That is: utility.h (for example) includes Array.h and StaticArray.h instead of them including one another.
Then you are free to put all definition code into the StaticArray.h file with no cross-include side-effects (and thus fix the unresolved external).
Related
I want an array of objects prefixed with size/capacity. My requirements are:
The array elements should to be constructed on demand, like std::vector.
This object itself will be shared (i.e., heap allocated), so using std::vector instead would imply 2 levels of indirection and 2 allocations, which I have to avoid.
Requirements up to this point are non-negotiable. Here's my sample to get the rough idea of what I'd do:
#include <cassert>
#include <cstdio>
#include <string>
template <class T>
struct Array {
private:
int size_;
int capacity_;
alignas(T) unsigned char data_[];
Array() = default;
public:
Array(Array const&) = delete;
Array& operator=(Array const&) = delete;
static auto newArr(int capacity) {
auto p = new unsigned char[sizeof(Array) + capacity * sizeof(T)];
auto pObj = new (p) Array;
pObj->size_ = 0;
pObj->capacity_ = capacity;
return pObj;
}
static auto deleteArr(Array* arr) {
if (!arr) return;
for (int i = 0; i != arr->size_; ++i) arr->get(i).~T();
arr->~Array();
delete[] reinterpret_cast<unsigned char*>(arr);
}
auto& get(int index) {
return reinterpret_cast<T&>(data_[index * sizeof(T)]);
}
auto push_back(T const& t) {
assert(size_ < capacity_);
new (&get(size_++)) T(t);
}
};
int main() {
auto arr = Array<std::string>::newArr(5);
for (int i = 0; i != 3; ++i) {
arr->push_back(std::to_string(i));
}
for (int i = 0; i != 3; ++i) {
std::printf("arr[%d] = %s\n", i, arr->get(i).c_str());
}
Array<std::string>::deleteArr(arr);
}
It uses a flexible-array-member, which is an extension and that's OK by me (works in GCC and clang? then it's OK). But it is not constexpr friendly because it necessarily uses:
placement new, not allowed in constexpr context for some reason, even though that's surely what allocators do. We can't replace it with an allocator because they don't support the flexible array member trick.
reinterpret_cast to access the elements as T and to free the memory at the end.
My question:
How do I satisfy the previously mentioned requirements and keep the class constexpr friendly?
Ultimately, what you're trying to do is create a contiguous series of objects in unformed memory that isn't defined by a single, valid C++ struct or by a C++ array of Ts. Constexpr allocations cannot do that.
You can allocate a byte array in constexpr code. But you cannot subsequently do any of the casting that normal C++ would require in order to partition this memory into a series of objects of different types. You can allocate storage suitable for an array of Array<T> objects. Or you can allocate storage suitable for an array of T objects. But std::allocator<T>::allocate will always return a T*. And constexpr code doesn't let you cast this pointer to some other, unrelated type.
And without being able to do this cast, you cannot later call std::construct_at<T>, since the template parameter T must match the pointer type you give it.
This is of course by design. Every constexpr allocation of type T must contain zero or more Ts. That's all it can contain.
Considering #NicolBolas's answer, this can't be done as is, but if you can afford an indirection, it can be done. You do separate allocations if the object is constructed at compile-time where performance concern doesn't exist, and do the single-allocation + reinterpret_cast trick if constructed at runtime:
#include <cassert>
#include <new>
#include <type_traits>
#include <memory>
template <class T>
struct ArrayData {
protected:
int size_ = 0;
int capacity_ = 0;
T *buffer;
};
template <class T>
struct alignas(std::max(alignof(T), alignof(ArrayData<T>))) Array
: ArrayData<T> {
private:
constexpr Array() = default;
using alloc = std::allocator<T>;
using alloc_traits = std::allocator_traits<alloc>;
public:
Array(Array const &) = delete;
Array &operator=(Array const &) = delete;
constexpr static auto newArr(int capacity) {
if (std::is_constant_evaluated()) {
auto arr = new Array<T>();
alloc a;
T *buffer = alloc_traits::allocate(a, capacity);
arr->capacity_ = capacity;
arr->buffer = buffer;
return arr;
} else {
auto p = new unsigned char[sizeof(Array) + capacity * sizeof(T)];
auto pObj = new (p) Array;
pObj->capacity_ = capacity;
pObj->buffer = std::launder(reinterpret_cast<T *>(pObj + 1));
return pObj;
}
}
constexpr static auto deleteArr(Array *arr) noexcept {
if (!arr) return;
auto p = arr->buffer;
for (int i = 0, size = arr->size_; i != size; ++i)
std::destroy_at(p + i);
if (std::is_constant_evaluated()) {
auto capacity = arr->capacity_;
delete arr;
alloc a;
alloc_traits::deallocate(a, p, capacity);
} else {
arr->~Array();
delete[] reinterpret_cast<unsigned char *>(arr);
}
}
constexpr auto &get(int index) { return this->buffer[index]; }
constexpr auto push_back(T const &t) {
assert(this->size_ < this->capacity_);
std::construct_at(this->buffer + this->size_++, t);
}
};
constexpr int test() {
auto const size = 10;
auto arr = Array<int>::newArr(size);
for (int i = 0; i != size; ++i) arr->push_back(i);
int sum = 0;
for (int i = 0; i != size; ++i) sum += arr->get(i);
Array<int>::deleteArr(arr);
return sum;
}
int main() {
int rt = test();
int constexpr ct = test();
return rt == ct;
}
Here's another approach. I've used unions to make it possible for subobjects of a single array object to have different types and different lifetimes. I'm not entirely sure if everything's strictly legal, but the major compilers don't complain.
I did need to have newArr return an object rather than pointer. If that's not an issue, it would probably make more sense to just give FlexArray an actual constructor and destructor.
#include <memory>
#include <cassert>
template <typename T>
class FlexArray
{
public:
FlexArray(const FlexArray&) = delete;
FlexArray& operator=(const FlexArray&) = delete;
static constexpr FlexArray newArr(unsigned int capacity);
constexpr void deleteArr();
constexpr unsigned int size() const;
constexpr unsigned int capacity() const;
constexpr T& get(unsigned int index);
constexpr void push_back(T const& obj);
private:
struct Header {
unsigned int size;
unsigned int capacity;
};
static constexpr auto T_per_node =
(sizeof(T) < sizeof(Header)) ? sizeof(Header)/sizeof(T) : 1U;
union MaybeT {
unsigned char dummy;
T obj;
constexpr MaybeT() {}
explicit constexpr MaybeT(const T& src) : obj(src) {}
constexpr ~MaybeT() {}
};
union U {
Header head;
MaybeT data[T_per_node];
constexpr U() : data{} {}
constexpr ~U() {}
};
U* nodes;
explicit constexpr FlexArray(U* n) : nodes(n) {}
};
template <typename T>
constexpr FlexArray<T> FlexArray<T>::newArr(unsigned int capacity)
{
auto new_nodes = new U[1 + (capacity + (T_per_node-1))/T_per_node];
new_nodes[0].head = {0, capacity};
return FlexArray{new_nodes};
}
template <typename T>
constexpr void FlexArray<T>::deleteArr()
{
unsigned int i = size();
while (i--) {
get(i).~T();
}
delete[] nodes;
nodes = nullptr;
}
template <typename T>
constexpr unsigned int FlexArray<T>::size() const
{
return nodes[0].head.size;
}
template <typename T>
constexpr unsigned int FlexArray<T>::capacity() const
{
return nodes[0].head.capacity;
}
template <typename T>
constexpr T& FlexArray<T>::get(unsigned int index)
{
return nodes[1 + index / T_per_node].data[index % T_per_node].obj;
}
template <typename T>
constexpr void FlexArray<T>::push_back(const T& obj)
{
assert(size() < capacity());
auto index = nodes[0].head.size++;
MaybeT *addr = nodes[1 + index / T_per_node].data + (index % T_per_node);
addr->~MaybeT();
std::construct_at(addr, obj);
}
#include <functional>
constexpr int test()
{
int a = -1;
int b = 2;
int c = 5;
auto arr = FlexArray<std::reference_wrapper<int>>::newArr(3);
arr.push_back(std::ref(a));
arr.push_back(std::ref(b));
arr.push_back(std::ref(c));
arr.get(1).get() = 7;
auto sum = arr.get(0) + b + arr.get(2);
arr.deleteArr();
return sum;
}
static_assert(test() == 11);
See it on godbolt.
I am trying to code a template array class and overloading some operators. Part of my code is as follows:
template.h:
main.cpp:
C2679 binary '<': no operator found which takes a right-hand operand of type 'Array<int>' (or there is no acceptable conversion)
What is causing this error?
What is causing this error?
You are using
return this < a;
this is a pointer while a is a reference to an object. It's analgous to comparing an int* with an int.
int a = 10;
int b = 11;
int* p = &b;
if ( p < a ) { ... }
That is not right.
That function needs to be implemented differently. You need to compare each item of the arrays and return an appropriate value.
template<typename T>
bool Array<T>::operator<(const T& a)
{
int lowerLength = std::min(this->arrLength, a.arrLengh);
for ( int i = 0; i < lowerLength; ++i )
{
if ( this->myArray[i] != a.myArray[i] )
{
return (this->myArray[i] < a.myArray[i]);
}
}
// If we get here, return a value based on which array has more elements.
return (this->arrLength < a.arrLengh)
}
While at it, make the member function a const member function.
bool Array<T>::operator<(const T& a) const;
and change the implementation accordingly.
In findBigPos() (and your other functions in Driver.cpp, too), you should be passing arr by reference, not by pointer. When arr is a pointer, arr[index] is the same as *(arr + index) - it performs pointer arithmetic to dereference the pointer at a given offset, it does not index into your array at all. That is why the compiler thinks you are comparing Array<int> objects, and not calling your operator[].
Try this instead:
#include "wallet.h"
#include "currency.h"
#include "array.h"
#include <iostream>
#include <string>
using namespace std;
template<typename T>
void recurSelectionSort(Array<T>&, int size, int index);
template<typename T>
int findBigPos(Array<T>&, int size, int index);
int main() {
//code
}
template<typename T>
void recurSelectionSort(Array<T>& arr, int size, int index) // move the biggest element in arr to index
{
if (index == size) {
return;
}
else if (index < size) {
int bigPos = findBigPos(arr, size, index); //position of "biggest" element
T bigVal = arr[bigPos]; //the value of "biggest" element
T copy = arr[index]; //copy of wat ever is going to get copy
arr[index] = bigVal;
arr[bigPos] = copy;
recurSelectionSort(arr, size, index + 1);
cout << arr;
}
}
template<typename T>
int findBigPos(Array<T>& arr, int size, int index)
{
if (index == size - 1) {
return index;
}
else
{
int bigPos = findBigPos(arr, size, index + 1);
return arr[bigPos] < arr[index] ? index : bigPos;
}
}
That said, there are some issues with your Array class itself, too.
You are not implementing the Rule of 3/5/0. Your class is lacking a copy constructor and a copy assignment operator, and in C++11 and later a move constructor and a move assignment operator.
you don't have a const version of your operator[] for your operator<< to use, since it takes a reference to a const Array<T> as input.
your operator[] is not checking for index < 0. And it would be better to throw a std::out_of_range exception instead of an int. If it throws at all. Typically, an array's operator[] should not perform bounds checking at all. That is why containers like std::vector and std::string have a separate at() method for handling bounds checking.
your operator< is not implemented correctly at all. You can't compare a Array<T>* pointer to a const T& reference. You probably meant to dereference the this pointer before comparing it to a, but then that would lead to an endless recursive loop. What you should do instead is change const T& a to const Array<T> &a and then compare the contents of this to the contents of a.
Try this:
#ifndef ARRAY_HEADER
#define ARRAY_HEADER
#include <iostream>
#include <stdexcept>
#include <utility>
template<typename T>
class Array
{
private:
int arrLength;
T* myArray;
public:
Array(int length = 5);
Array(const Array &a);
Array(Array &&a);
virtual ~Array();
int getLength() const;
Array& operator=(Array a);
T& operator[](int index);
const T& operator[](int index) const;
bool operator<(const Array &a) const;
friend std::ostream& operator<<(std::ostream &output, const Array &arr)
{
int arrSize = arr.getLength();
for (int i = 0; i < arrSize; i++) {
output << arr[i] << " ";
}
return output;
}
};
template<typename T>
Array<T>::Array(int length)
{
myArray = new T[length];
arrLength = length;
}
template<typename T>
Array<T>::Array(const Array<T> &a)
{
myArray = new T[a.arrLength];
arrLength = a.arrLength;
for(int i = 0; i < arrLength; ++i)
myArray[i] = a.myArray[i];
}
template<typename T>
Array<T>::Array(Array<T> &&a)
{
arrLength = a.arrLength;
myArray = a.myArray;
a.myArray = nullptr;
a.arrLength = 0;
}
template<typename T>
Array<T>::~Array()
{
delete[] myArray;
}
template<typename T>
int Array<T>::getLength() const
{
return arrLength;
}
template<typename T>
Array<T>& Array<T>::operator=(Array<T> a)
{
using std::swap;
swap(myArray, a.myArray);
swap(arrLength, a.arrLength);
return *this;
}
template<typename T>
T& Array<T>::operator[](int index) {
if ((index < 0) || (index >= arrLength)) {
throw std::out_of_range("index is out of range");
}
return myArray[index];
}
template<typename T>
const T& Array<T>::operator[](int index) const {
if ((index < 0) || (index >= arrLength)) {
throw std::out_of_range("index is out of range");
}
return myArray[index];
}
template<typename T>
bool Array<T>::operator<(const Array<T> &a) const
{
if (arrLength < a.arrLength)
return true;
if (arrLength == a.arrLength)
{
for (int i = 0; i < arrLength; ++i)
{
if (myArray[i] != a.myArray[i])
return myArray[i] < a.myArray[i];
}
}
return false;
}
#endif
I'm in the early stage of trying to convert an array class containing operator overloading into a templatized class. At the moment I'm trying to add the template definition to the class and each function. This should be fairly simple however, whenever I run the program I get a scope error.
The compiler says `T' was not declared in this scope (I will comment the error on the line that it occurs). The error also re-occurs on the other function definitions. I'm using a program invovling a templatized class as a guide and it implements the functions in the exact manner that I am trying to(which is why I'm confused). What do I need to do to resolve this?
Thank you.
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <stdexcept>
using namespace std;
#ifndef ARRAY_H
#define ARRAY_H
template <typename T>
class Array
{
public:
Array(int = 10);
Array(const Array&);
~Array();
int getSize() const;
const Array &operator=(const Array &);
bool operator==(const Array&) const;
bool operator!=(const Array &right) const
{
return ! (*this == right);
}
int &operator[](int);
int operator[](int) const;
private:
int size;
int *ptr;
};
#endif
template<typename t>
Array<T>::Array(int arraySize) //ERROR: T was not declared in this scope***********
{
if(arraySize > 0)
size = arraySize;
else
throw invalid_argument("Array size myst be greater than 0");
ptr = new int[size];
for(int i = 0; i < size; i++)
ptr[i] = 0;
}
template<typename t>
Array<T>::Array(const Array &arrayToCopy): size(arrayToCopy.size)
{
ptr = new int[size];
for(int i = 0; i < size; i++)
ptr[i] = arrayToCopy.ptr[i];
}
template<typename t>
Array<T>::~Array()
{
delete [] ptr;
}
template<typename t>
int Array<T>::getSize() const
{
return size;
}
template<typename t>
const Array<T> &Array::operator=(const Array &right)
{
if (&right != this)
{
if(size != right.size)
{
delete [] ptr;
size = right.size;
ptr = new int[size];
}
for(int i = 0; i < size; i++)
ptr[i] = right.ptr[i];
}
return *this;
}
template<typename t>
bool Array<T>::operator==(const Array &right) const
{
if(size != right.size)
return false;
for(int i = 0; i < size; i++)
if(ptr[i] != right.ptr[i])
return false;
return true;
}
template<typename t>
int &Array<T>::operator[](int subscript)
{
if(subscript < 0 || subscript >= size)
throw out_of_range("Subscript out of range");
return ptr[subscript];
}
template<typename t>
int Array<T>::operator[](int subscript) const
{
if(subscript < 0 || subscript >= size)
throw out_of_range("Subscript out of range");
return ptr[subscript];
}
int main()
{
//main is empty at the moment because I want to make sure that the class is functional
//before implementing the driver function.
system("pause");
}
Before each of your function definitions, you have written:
template<typename t>
You mean:
template<typename T>
That is, it should match the template parameter of your class, which is a capital T.
i`m working on my assignment for univ, and since some parts are not really good explained i got some problems there is my structure and my constructor for it, it has to be dynamical but i get the fallowing error. Some help is really appreciated thank you.
.h:
const int days=31;
const int exp=6;
struct Array{
int days;
int exp;
int **M;
};
.cpp:
void constr(Array &loc){
//Construct of 31*6 Matrix, were 31 nr. of days and 6 specific types:
//0-HouseKeeping, 1-Food, 2-Transport, 3-Clothing, 4-TelNet, 5-others
loc.days = days;
loc.exp = exp;
loc.M=malloc(loc.days*sizeof(int*));
for(int i=0; i<loc.days;i++ ){
loc.M[i] = malloc(loc.exp*sizeof(int));
for (int j = 0; j< loc.exp; j++){
loc.M[i][j] = 0;
}
}
}
error:
..\src\structs.cpp: In function 'void constr(Array&)':
..\src\structs.cpp:7:36: error: invalid conversion from 'void*' to 'int**' [-fpermissive]
..\src\structs.cpp:9:40: error: invalid conversion from 'void*' to 'int*' [-fpermissive]
Since you asked for C++ constructors in your comment... See the code below. I also replaced your two-dimensional C-style array with a C++ vector. I added code comments to the relevant lines:
Array.h:
#pragma once
#include <vector>
struct Array
{
// this is a c++ constructor declaration
Array(int daysParam, int expParam);
int days;
int exp;
// use a vector of vectors instead allocating with new or malloc
// it is easier to initialize and the compiler will clean it up for you
std::vector<std::vector<int> > M;
};
Array.cpp:
#include "Array.h"
// Array constructor definition with initializer list
// all data members are initialized here by invoking their constructor
Array::Array(int daysParam, int expParam)
: days(daysParam),
exp(expParam),
M(daysParam, std::vector<int>(expParam, 0))
{
}
Example for usage of Array (Program.cpp):
#include "Array.h"
int main()
{
// create a new Array, using the c++ constructor
Array myArray(31, 6);
// access elements in the 2-dimensional array
int singleValue = myArray.M[15][3];
return 0;
}
I strongly advise you to read a book about C++
Since this is C++:
loc.M = new int*[loc.days];
for(int i=0; i<loc.days;i++ ){
loc.M[i] = new int[loc.exp];
for (int j = 0; j< loc.exp; j++){
loc.M[i][j] = 0;
}
}
loc.M = (int**)malloc(loc.days*sizeof(int*));
loc.M[i] = (int*)malloc(loc.exp*sizeof(int));
Please, stop using std::vector > or, worse T tab[][] for representing a 2D array. You should use a 1D array to store data, a an index array to store row pointers. That way, your data remains contiguous, and you still can have a nice syntax.
template<typename T>
class Array2D
{
std::vector<T> m_data;
std::vector<T*> m_ptr;
size_t m_iWidth;
size_t m_iHeight;
void Link(void)
{
for (unsigned int j = 0; j < m_iHeight; ++j)
m_ptr[j] = &m_data[j * m_iWidth];
}
public:
Array2D(void)
{
};
Array2D(const size_t i_width, const size_t i_height) :
m_iWidth(i_width),
m_iHeight(i_height),
m_data(i_width * i_height),
m_ptr(i_height)
{
Link();
}
void Resize(const size_t niou_width, const size_t niou_height)
{
if (m_iWidth == niou_width && m_iHeight == niou_height)
return;
m_iWidth = niou_width;
m_iHeight = niou_height;
m_data.resize(niou_height * niou_width);
m_ptr.resize(niou_height);
Link();
}
typename std::vector<T>::iterator begin(void)
{
return m_data.begin();
}
typename std::vector<T>::iterator end(void)
{
return m_data.end();
}
void assign(T value)
{
m_data.assign(m_iWidth * m_iHeight, value);
}
Array2D(const Array2D& a) :
m_iWidth(a.m_iWidth),
m_iHeight(a.m_iHeight),
m_data(a.m_data)
{
m_ptr.resize(m_iHeight);
Link();
}
Array2D& operator=(const Array2D a)
{
swap(*this, a);
return *this;
}
template <typename U>
friend void swap(Array2D<U>& first, Array2D<U>& second)
{
using std::swap;
swap(first.m_iHeight, second.m_iHeight);
swap(first.m_iWidth, second.m_iWidth);
swap(first.m_data, second.m_data);
swap(first.m_ptr, second.m_ptr);
}
~Array2D()
{
};
T* operator[](const size_t ligne)
{
return m_ptr[ligne];
};
const T* operator[](const size_t ligne) const
{
return m_ptr[ligne];
};
T& operator()(const size_t col, const size_t lig)
{
return m_ptr[lig][col];
};
const T& operator()(const size_t col, const size_t lig) const
{
return m_ptr[lig][col];
};
I'm pretty new to C++ and this site so there are bound to be errors. When I try to compile my code I get errors like error: missing template argument before 'b'. I've been searching the world for answers for hours and it has led me here.
My assignment is to implement a templated class Collection that stores a collection of
Objects using an array, along
with the current size of the collection.
#include <iostream>
#include "collection.h"
using namespace std; v
int main(int argc, char* argv[])
{
collection b; //<----error missing template argument before 'b'
return 0;
}
#ifndef COLLECTION_H
#define COLLECTION_H
#include <iostream>
template <typename obj>
class collection
{
public:
collection();
bool isEmpty() const;
void makeEmpty();
void insert(obj val);
void remove(obj val);
bool contains(obj val) const;
private:
size_t size;
obj* col[];
};
#endif
#include "collection.h"
template <typename obj>
collection<obj>::collection() :size(10)
{
col = new obj*[size];
}
template <typename obj>
bool collection<obj>::isEmpty() const
{
for(size_t k = 0; k < size; k++)
{
if(col[k] != NULL)
return false;
}
return true;
}
template <typename obj>
void collection<obj>::makeEmpty()
{
for(size_t k = 0; k < size; k++)
{
col[k] = NULL;
}
}
template <typename obj>
void collection<obj>::insert(obj val)
{
int temp = 0;
for(size_t s = 0; s < size; s++)
{
if(col[s] != NULL)
temp++;
}
if(temp >= size)
{
obj* temp = new obj*[size*2];
for(size_t c = 0; c < size; c++)
temp[c] = col[c];
delete col;
col = temp;
}
else
col[temp] = val;
}
template <typename obj>
void collection<obj>::remove(obj val)
{
for(size_t x = 0; x < size; x++)
{
if (col[x] == val)
{
for(size_t y = x; y < size-1; y++)
{
col[y] = col[y+1];
}
col[size-1] = NULL;
return;
}
}
}
template <typename obj>
bool collection<obj>::contains(obj val) const
{
for(size_t z = 0; z < size; z++)
{
if(col[z] == val)
return true;
}
return false;
}
You have to say what it's a collection of.
template <class A> class collection {}
requires that you use it as
collection<int> b;
or some appropriate type. That then makes a collection of ints. You haven't told it what you want a collection of.
First : Instantiate template by type. So if you have template <typename obj> class T {...}; you should use it like
void main {
T<int> t;
T<bool> t1; // .. etc
}
You can use a template with default value for the typename parameter defined in the class template declaration
template <typename obj = int> class T {/*...*/};
void main {
T<> t;
}
but anyway you should put empty angle brackets when use it without parameter.
Second: While declaring template, place it whole in the header file. Each definition of his methods should be in the file "*.h", don't ask me why, just don't split it to the header and "cpp" file.
Hope it helps.
Well, you're missing a template argument. You can't create a collection object, that's just a template.
You can only create e.g. a collection<int> or collection<std::string>.
You need to specify the type for template, like int or some other type:
collection<int> b;
There are a large number of errors in your code. First define your template in a header file:
In collection.h put the following:
#ifndef COLLECTION_H
#define COLLECTION_H
template <typename obj>
class collection
{
public:
collection() {}
bool isEmpty() const;
void makeEmpty();
void insert(obj val);
void remove(obj val);
bool contains(obj val) const;
private:
size_t size;
obj* col[];
};
#endif
Then in a .cpp file inside a main function do the following:
collection<int> b;
Instead of int you can use a different type but the main point is that YOU NEED A TYPE to instantiate a template.