As an exercise to learn a little more about dynamic memory allocation in C++, I'm working on my own vector class. I'm running into a little difficulty overloading the sum operator, so I thought I'd turn here to get some insight into why this doesn't work. Here's what I have so far:
template<typename T>
class vector
{
private:
T* pointer_;
unsigned long size_;
public:
// Constructors and destructors.
vector();
template<typename A> vector(const A&);
template<typename A> vector(const A&, const T&);
vector(const vector<T>&);
~vector();
// Methods.
unsigned long size();
T* begin();
T* end();
vector<T>& push_back(const T&);
// Operators.
T operator[](unsigned long);
vector<T>& operator=(const vector<T>&);
friend vector<T>& operator+(const vector<T>&, const vector<T>&);
};
The template<typename A> vector(const A&) constructor looks like this:
template<typename T> template<typename A>
vector<T>::vector(const A& size)
{
this->pointer_ = new T[size];
this->size_ = size;
}
Finally, the operator+ operator looks like this:
template<typename T>
vector<T>& operator+(const vector<T>& lhs, const vector<T>& rhs)
{
vector<T> result(lhs.size_);
for (unsigned long i = 0; i != result.size_; i++)
{
result.pointer_[i] = lhs.pointer_[i] + rhs.pointer_[i];
}
return result;
}
My compiler (VS2013) returns an unresolved external symbol error when I try to compile this code, which (as I understand it) means there's a function declared somewhere that I haven't actually defined. I'm not sure where the problem is, however: the template<typename A> vector(const A&) constructor works fine. What am I missing?
You're not properly friending the template operator function. There are multiple ways to do this, each of which has advantages. One is the friend-the-world ideology where all expansions of the template are friends with each other. I prefer to be a bit more restrictive than that.
Above your vector class, do this:
template<typename T>
class vector;
template<typename T>
vector<T> operator+(const vector<T>& lhs, const vector<T>& rhs);
The proceed as before, but note the syntax in the friend declaration:
// vector class goes here....
template<typename T> class vector
{
.... stuff ....
friend vector<T> operator+<>(const vector<T>&, const vector<T>&);
};
Then define the rest as you have it. That should get you what I think you're trying to achieve.
Best of luck.
PS: invalid reference return value fixed in the above code.
Related
I am quite new to C++ and I am implementing a, quite simple, (linear algebra) vector class as a template.
Now, I am trying to implement the addition and subtraction operators but with the ability to perform the operations not only between vectors, but also with scalars (short, long, float, double, etc.) (this will be an element-wise operation).
The relevant part of the code can be seen below and both declaration and definition (implementation) of the template class is in the same .hpp file (checked this question and didn't seem to provide a solution for this exact reason).
// Forward declaration of friend function
template<class S, class T>
Vector<T> operator +(const S& scalar, const Vector<T>& vec);
template<class T>
class Vector {
public:
Vector(unsigned long length);
Vector(Vector<T> vec);
template<class S>
friend Vector<T> operator +(const S& scalar, const Vector<T>& vec);
template<class S>
Vector<T> operator +(const S& scalar) const;
template<class S>
Vector<T>& operator +=(const S& scalar);
private:
unsigned long mLen;
T* mData;
};
// Constructor
Vector<T>::Vector(const unsigned long length) : mLen(length) {
mData = new T[mData];
for(unsigned long i = 0; i < mLen; ++i) {
mData[i] = T();
}
}
// Copy constructor
template<class T>
Vector<T>::Vector(Vector<T> vec) {
mLen = 0;
mData = nullptr;
std::swap(mData, vec.mData);
}
// Implementation of the operators
template<class S, class T>
Vector<T> operator +(const S& scalar, const Vector<T>& vec) {
return vec + scalar;
}
template<class T> template<class S>
Vector<T> operator +(const S& scalar) const {
Vector<T> result(*this);
return result += scalar;
}
template<class T> template<class S>
Vector<T>& Vector<T>::operator +(const S& scalar) {
// mLen is the length of the object (member variable)
for(unsigned long i = 0; i < mLen; ++i) {
mData[i] += scalar; // mData is the array holding the values (member variable)
}
return *this;
}
The addition between a Vector and a scalar is implemented (as a member function though) and works as intended (result-wise). Furthermore, I can guarantee (for the moment at least) that S will always be one of short, int, float or double.
So, when in my tests I try to do something like
Vector<int> vec(10);
1 + vec;
I get the following error
Undefined symbols for architecture x86_64:
"operator+(int const&, Vector const&)", referenced from:
_main in main.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
I am working on XCode 11.3.1 for this project.
Any insights would be most welcome. Additionally, I know that my understanding of templates (and a whole lot more about C++) is still very limited, so if you would require more information to get a better idea please let me know.
Thanks in advance.
First, the free function template doesn't need to be a friend since it's not directly accessing the private members in Vector<T>. It also doesn't need to be forward declared. This can also be greatly simplified by using a std::vector<T> to keep the data to not have to implement the rule of 5 manually.
Example:
#include <vector>
template<class T>
class Vector {
public:
Vector(unsigned long length) :
mData(length) // create the vector
{}
template<class S>
Vector& operator+=(S scalar) {
for(auto& v : mData) v += scalar; // add "scalar" to all elements in the vector
return *this;
}
template<class S>
Vector operator+(S s) const {
Vector copy(*this); // using the implicitly defined copy constructor
copy += s; // use the above operator+=
return copy;
}
private:
std::vector<T> mData;
};
template<class S, class T>
Vector<T> operator +(S scalar, const Vector<T>& vec) {
return vec + scalar;
}
I am implementing the addition operator for a custom class. The function/operator invoked is neither a friend or a member function and adds a scalar value to the vector (the class is supposed to be a linear algebra vector class). Internally, this function calls the addition operator of the class which in turn calls the compound addition of the same class.
Below is the relevant part of the declaration of the class (this is a template class) along with the implementation of the constructors, the non-member function/operator and the compound addition operator (the addition operators follow further below to show the "issue").
template<class T>
class Vector {
public:
Vector(const unsigned long length); // Default ctor
Vector(const Vector<T>& vec); // Copy ctor
template<class S>
Vector<T> operator +(const S& scalar);
template<class S>
Vector<T>& operator +=(const S& scalar);
private:
unsigned long mLen;
T* mData;
};
template<class T, class S>
Vector<T> operator +(const S& scalar, const Vector<T>& vec);
// Implementation of ctors
template<class T>
Vector<T>::Vector(const unsigned long length) {
mLen = length;
mData = new T[length];
}
template<class T>
Vector<T>::Vector(const Vector<T>& vec) {
mLen = vec.mLen;
std::copy_n(vec.mData, vec.mLen, mData);
}
template<class T, class S>
Vector<T> operator +(const S& scalar, const Vector<T>& vec) {
return vec + scalar;
}
template<class T> template<class S>
Vector<T>& Vector<T>::operator +=(const S& scalar) {
for(unsigned long i = 0; i < mLen; ++i) {
mData[i] += scalar;
}
return *this;
}
As already stated, the implementation is based on the compound assignment operator. All operators seem to be working as intended, result-wise, but when I return the result of the compound assignment operation directly the copy constructor is invoked and I can't understand why. See the following examples for clarification.
Example where the copy constructor is invoked
template<class T> template<class S>
Vector<T> Vector<T>::operator +(const S& scalar) {
Vector<T> result(*this); // Normal copy ctor call
return result += scalar; // Here the copy ctor is invoked again!!!
}
Example where the copy constructor is not invoked
// Implementation of the addition operators
template<class T> template<class S>
Vector<T> Vector<T>::operator +(const S& scalar) {
Vector<T> result(*this); // Normal copy ctor call
result += scalar;
return result; // Here the copy ctor is NOT invoked
}
A main.cpp example could be the following
int main(int argc, const char* argv[]) {
Vector<T> vec(10);
1 + vec;
}
Now, when the first implementation of the addition operator is used, the copy constructor is called at the return statement (in addition to the line above which obviously makes perfect sense). When the operator is implemented in the second way, the copy constructor is only called once, when explicitly called in the above line.
I am using XCode 11.3.1 (on Mac obviously). Please let me know if you would like more information on the issue. If someone could shed some light here I would be grateful.
Since operator+= returns a reference, the return statement needs to copy this referenced value into the return value for the function. There is not a value available that the compiler can use to elide this call to the copy constructor.
Your first example is the equivalent of
template<class T> template<class S>
Vector<T> Vector<T>::operator +(const S& scalar) {
Vector<T> result(*this);
Vector<T> &ans = (result += scalar); // (Parens included for clarity)
return ans;
}
which should make it more obvious why the compiler must call the copy constructor in the return statement.
The second example returns the local result directly, so the copy constructor call is elided.
Does anyone know why this will not compile and how to correct it? I'm trying to use friends and templates. I'm using this code from Stroustrup C++ 4th Ed Page 682-683.
Thanks
#include <iostream>
using namespace std;
template<typename T>
class Matrix;
template<typename T>
class Vector
{
T v[4];
public:
friend Vector operator*<>(const Matrix<T>&, const Vector&);
};
template<typename T>
class Matrix
{
Vector<T> v[4];
public:
friend Vector<T> operator*<>(const Matrix&, const Vector<T>&);
};
template<typename T>
Vector<T> operator*(const Matrix<T>& m, const Vector<T>& v)
{
Vector<T> r;
}
int main(int argc, char *argv[])
{
}
Compilation:
clang++ -std=c++11 -pedantic -Wall -g test165.cc && ./a.out
test165.cc:12:19: error: friends can only be classes or functions
friend Vector operator*<>(const Matrix<T>&, const Vector&);
^
test165.cc:12:28: error: expected ';' at end of declaration list
friend Vector operator*<>(const Matrix<T>&, const Vector&);
^
;
test165.cc:19:22: error: friends can only be classes or functions
friend Vector<T> operator*<>(const Matrix&, const Vector<T>&);
^
test165.cc:19:31: error: expected ';' at end of declaration list
friend Vector<T> operator*<>(const Matrix&, const Vector<T>&);
^
The friend declaration refers to the instantiation of template operator* (i.e. operator*<T>), but the template doesn't exist (has not been declared) and then cause the error.
You need to declare the operator template in advance.
E.g.
template<typename T>
class Matrix;
template<typename T>
class Vector;
// declaration
template<typename T>
Vector<T> operator*(const Matrix<T>& m, const Vector<T>& v);
template<typename T>
class Vector
{
T v[4];
public:
friend Vector operator*<>(const Matrix<T>&, const Vector&);
};
template<typename T>
class Matrix
{
Vector<T> v[4];
public:
friend Vector<T> operator*<>(const Matrix&, const Vector<T>&);
};
// definition
template<typename T>
Vector<T> operator*(const Matrix<T>& m, const Vector<T>& v)
{
Vector<T> r;
}
This Vec template supports several functions such as multiplying a vector by scalar and adding vector to another vector.
The thing that is confusing me is that why the overloading of the second operator* is outside of the class template?
The operator* which is declared in the class overloads vectorXscalar
The one declared outside supports scalarXvector
template <class T>
class Vec {
public:
typename list<T>::const_iterator begin() const {
return vals_.begin();
}
typename list<T>::const_iterator end() const {
return vals_.end();
}
Vec() {};
Vec(const T& el);
void push_back(T el);
unsigned int size() const;
Vec operator+(const Vec& rhs) const;
Vec operator*(const T& rhs) const; //here
T& operator[](unsigned int ind);
const T& operator[](unsigned int ind) const;
Vec operator,(const Vec& rhs) const;
Vec operator[](const Vec<unsigned int>& ind) const;
template <class Compare>
void sort(Compare comp) {
vals_.sort(comp);
}
protected:
list<T> vals_;
};
template <class T>
Vec<T> operator*(const T& lhs, const Vec<T>& rhs); //and here!
template <class T>
ostream& operator<<(ostream& ro, const Vec<T>& v);
The operator* declared inside the template class could equally be written outside the class as
template <class T>
Vec<T> operator*(const Vec<T>& lhs, const T& rhs);
It can be written inside the class with a single argument (representing the rhs) because there is the implied *this argument used as the lhs of the operator.
The difference with the operator* defined outside the class is that the lhs of the operator is a template type. This allows the arguments to be supplied either way around when using the operator.
You are allowed to define the operator outside of a class with any arbitrary lhs and rhs types, but within the class are restricted to only varying the rhs. The compiler would select the best match of any defined operator* given the argument types.
Related to this question that I asked yesterday.
I'm building my own vector class to learn a little more about C++, and I'm having some difficulty implementing an overloaded operator+ that I can use to add vectors. Here's what my class looks like so far:
template<typename T>
class vector;
template<typename T>
vector<T> operator+(const vector<T>&, const vector<T>&);
template<typename T>
class vector
{
private:
T* pointer_;
unsigned long size_;
public:
// Constructors and destructors.
vector();
template<typename A> vector(const A&);
template<typename A> vector(const A&, const T&);
vector(const vector<T>&);
~vector();
// Methods.
unsigned long size();
T* begin();
T* end();
vector<T>& push_back(const T&);
// Operators.
T operator[](unsigned long);
vector<T>& operator=(const vector<T>&);
friend vector<T> operator+<>(const vector<T>&, const vector<T>&);
};
template<typename T>
vector<T> operator+(const vector<T>& lhs, const vector<T>& rhs)
{
vector<T> result(lhs.size_);
for (unsigned long i = 0; i != result.size_; i++)
{
result.pointer_[i] = lhs.pointer_[i] + rhs.pointer_[i];
}
return result;
}
The constructor vector(const A& size, const T& value) operates as expected, returning a vector with size elements initialized to value. operator= appears to be working, as well. When I try something like this, however, my program doesn't perform as expected:
#include <iostream>
#include "vector.h"
int main()
{
vector<int> test(5, 2); // Create a vector with five elements initialized to 2.
vector<int> test2(5, 3); // Create a vector with five elements initialized to 3.
std::cout << test.size() // Prints 5, as expected.
std::cout << test2.size() // Prints 5, as expected.
vector<int> try_result = test + test2;
std::cout << try_result.size() // Prints 0--why?
}
My question: if operator+ returns a copy of result (and not by reference, a problem that originally existed in my code), why does it appear that try_result still does not point to the proper location in the heap?
EDIT: Adding the constructor code (I've tested this, and it seems to work, but maybe for the wrong reasons):
template<typename T> template<typename A>
vector<T>::vector(const A& size)
{
this->pointer_ = new T[size];
this->size_ = size;
}
template<typename T> template<typename A>
vector<T>::vector(const A& size, const T& initial_value)
{
this->pointer_ = new T[size];
this->size_ = size;
for (unsigned long i = 0; i != this->size_; i++)
{
pointer_[i] = initial_value;
}
}
EDIT2: Adding the copy constructor.
template<typename T>
vector<T>::vector(const vector<T>& replicate_vector)
{
delete[] this->pointer_;
this->pointer_ = new T[replicate_vector.size_];
this->size_ = replicate_vector.size_;
for (unsigned long i = 0; i != this->size_; i++)
{
this->pointer_[i] = replicate_vector.pointer_[i];
}
}