I am writing the following templated vector class.
vectors.h:
#ifndef INCLUDE_HEADERS_VECTORS_H
#define INCLUDE_HEADERS_VECTORS_H
template<class Scalar> class Vector2D;
template<class Scalar> Vector2D<Scalar> operator+(Scalar a, Vector2D<Scalar> v);
template<class Scalar>
class Vector2D {
public:
Vector2D();
Vector2D(Scalar a, Scalar b);
Scalar getX();
Scalar getY();
Vector2D<Scalar> operator+(Scalar a);
friend Vector2D<Scalar> operator+ <>(Scalar a, Vector2D<Scalar> v);
private:
Scalar x, y;
};
#endif
vectors.cpp:
#include<headers/vectors.h>
template<class Scalar>
Vector2D<Scalar>::Vector2D() {
x = 0;
y = 0;
}
template<class Scalar>
Vector2D<Scalar>::Vector2D(Scalar a, Scalar b) {
x = a;
y = b;
}
template<class Scalar>
Scalar Vector2D<Scalar>::getX() {
return x;
}
template<class Scalar>
Scalar Vector2D<Scalar>::getY() {
return y;
}
template<class Scalar>
Vector2D<Scalar> Vector2D<Scalar>::operator+(Scalar a) {
return Vector2D<Scalar>(a + x, a + y);
}
template<class Scalar>
Vector2D<Scalar> operator+(Scalar a, Vector2D<Scalar> v) {
return Vector2D<Scalar>(a + v.x, a + v.y);
}
template class Vector2D<float>;
template Vector2D<float> operator+<float>(float a, Vector2D<float> v);
Main.cpp:
#include<headers/vectors.h>
#include<iostream>
int main(int argc, char* argv[]){
Vector2D<float> a(1.0, 3.0);
std::cout<<a.getX()<<" "<<a.getY()<<std::endl;
a = (float)2.0 + a;
std::cout<<a.getX()<<" "<<a.getY()<<std::endl;
a = a + (float)-2.0;
std::cout<<a.getX()<<" "<<a.getY()<<std::endl;
return 0;
}
When I try to compile it with g++ on Windows, I get the following error:
g++ -Iinclude -Iinclude/SDL2 -Iinclude/headers -Llib -o Main src/*.cpp -lmingw32 -lSDL2main -lSDL2 -lSDL2_image
In file included from src/Main.cpp:2:
include/headers/vectors.h:29:33: error: declaration of 'operator+' as non-function
29 | friend Vector2D<Scalar> operator+ <>(Scalar a, Vector2D<Scalar> v);
| ^~~~~~~~
include/headers/vectors.h:29:41: error: expected ';' at end of member declaration
29 | friend Vector2D<Scalar> operator+ <>(Scalar a, Vector2D<Scalar> v);
| ^
| ;
include/headers/vectors.h:29:43: error: expected unqualified-id before '<' token
29 | friend Vector2D<Scalar> operator+ <>(Scalar a, Vector2D<Scalar> v);
| ^
In file included from src/vectors.cpp:1:
include/headers/vectors.h:29:33: error: declaration of 'operator+' as non-function
29 | friend Vector2D<Scalar> operator+ <>(Scalar a, Vector2D<Scalar> v);
| ^~~~~~~~
include/headers/vectors.h:29:41: error: expected ';' at end of member declaration
29 | friend Vector2D<Scalar> operator+ <>(Scalar a, Vector2D<Scalar> v);
| ^
| ;
include/headers/vectors.h:29:43: error: expected unqualified-id before '<' token
29 | friend Vector2D<Scalar> operator+ <>(Scalar a, Vector2D<Scalar> v);
| ^
src/vectors.cpp:70:18: error: non-class, non-variable partial specialization 'operator+<Scalar>' is not allowed
70 | Vector2D<Scalar> operator+<Scalar>(Scalar a, Vector2D<Scalar> v) {
| ^~~~~~~~~~~~~~~~~
src/vectors.cpp: In instantiation of 'Vector2D<Scalar> operator+(Scalar, Vector2D<Scalar>) [with Scalar = float]':
src/vectors.cpp:96:69: required from here
src/vectors.cpp:71:35: error: 'float Vector2D<float>::x' is private within this context
71 | return Vector2D<Scalar>(a + v.x, a + v.y);
| ~~^
include/headers/vectors.h:37:16: note: declared private here
37 | Scalar x, y;
| ^
src/vectors.cpp:71:44: error: 'float Vector2D<float>::y' is private within this context
71 | return Vector2D<Scalar>(a + v.x, a + v.y);
| ~~^
include/headers/vectors.h:37:19: note: declared private here
37 | Scalar x, y;
| ^
mingw32-make: *** [MakeFile:2: all] Error 1
However, if I change the order of the declarations of the +
operator in vectors.h
like in the following, it will work just fine:
#ifndef INCLUDE_HEADERS_VECTORS_H
#define INCLUDE_HEADERS_VECTORS_H
template<class Scalar> class Vector2D;
template<class Scalar> Vector2D<Scalar> operator+(Scalar a, Vector2D<Scalar> v);
template<class Scalar>
class Vector2D {
public:
Vector2D();
Vector2D(Scalar a, Scalar b);
Scalar getX();
Scalar getY();
friend Vector2D<Scalar> operator+ <>(Scalar a, Vector2D<Scalar> v);
Vector2D<Scalar> operator+(Scalar a);
private:
Scalar x, y;
};
I am completely lost as to why this happens.
I tried to remove the templates and the problem goes away, so it must be related.
The issue comes from how the "word <" sequence is understood by the compiler.
It can be "word less_than" or "word start_of_template_parameters".
Most often there is no ambiguity. But here we are in the complex case where the word is identified as a function name.
The problem with functions is that:
It's why we must avoid to have a same name as template and as non-template.
2nd case, the friend is declared before the operator+
.
The only function seen before is of type template, the compiler has no choice, it must interpret the <
as the start of template parameters.
1st case, the friend is declared after the operator+
.
There is a function operator+
which is the most visible and not template. Although a template version is possible, the non-template version is assumed to be valid. The way to interpret "word <" is therefore "word less_than". Hence the error.
Since C++20, the standard has evolved to handle more cases. This problem no more exist for the clang compiler, I don't know for the others.
Just some notes about the code:
double
over float
. It has more precision and is often the fastest one. double
is the standard type for floating values.float
, you can add double
and long double
.