1

Let's say I have this class:

in Vector4.h:

#pragma once

template<typename T>
class Vector4
{
public:
T X;
T Y;
T Z;
T W;

Vector4();
Vector4(T X, T Y, T Z, T W);
~Vector4();

friend Vector4<T> operator*(const Vector4<T>& l, const Vector4<T>& r);
};

#include "Vector4.inl"

and in Vector4.inl:

template<typename T>
Vector4<T>::Vector4()
{
X = 0;
Y = 0;
Z = 0;
W = 0;
}

template<typename T>
Vector4<T>::Vector4(T X, T Y, T Z, T W)
{
   this->X = X;
   this->Y = Y;
   this->Z = Z;
   this->W = W;
}

template<typename T>
Vector4<T>::~Vector4()
{

}

template<typename T>
Vector4<T> operator*(const Vector4<T>& l, const Vector4<T>& r)
{
     return(Vector4<T>(l.X * r.X, l.Y * r.Y, l.Z * r.Z, l.W * r.W));
}

And when I use it somewhere like this:

Vector4<float> a, b;
a = a * b;

it gives me a LNK2019 unresolved external symbol What am I doing wrong?Is the syntax I'm using incorrect?

ulak blade
  • 2,515
  • 5
  • 37
  • 81
  • 2
    Really no need to use `friend` when it's not accessing private members. – chris Jul 24 '13 at 18:24
  • *What* symbol is undefined? – Some programmer dude Jul 24 '13 at 18:25
  • Can you give us the full linker error? – IdeaHat Jul 24 '13 at 18:26
  • The full linker error is: Error 4 error LNK2019: unresolved external symbol "class Vector4 __cdecl operator*(class Vector4 const &,class Vector4 const &)"referenced in function _main C:\Users\Sony\Desktop\LocalStorage\Untitled\Engine\_MAIN.obj – ulak blade Jul 24 '13 at 18:28
  • Are you building two projects that are statically linked together? – Mooing Duck Jul 24 '13 at 18:30
  • Yes I am, Vector4 is in my Base project which is a lib and I'm using it in the main one that is an exe – ulak blade Jul 24 '13 at 18:31
  • Vector4.inl is included at the end of Vector4.h and I've linked Base.lib(where Vector4 is) in my main project. – ulak blade Jul 24 '13 at 18:33
  • I think your friend function declaration doesn't declare a template and therefore doesn't match the definition. – dyp Jul 24 '13 at 18:35
  • @DYP could you explain further?What do you mean?It's inside the class brackets.Isn't this proper syntax?And in the definition you don't need to put Vector4:: before the name because it's a friend operator, right? – ulak blade Jul 24 '13 at 18:36
  • @DyP I agree that it isn't declaring the correct function to be a friend, but shouldn't it not matter? He is accessing only public members in the non-friended function. – IdeaHat Jul 24 '13 at 18:40
  • @Bogomil I think you cannot friend a specialization of a template that has not been declared yet. Either friend the whole function template `template friend Vector4 operator*(...)` or use forward-declarations. – dyp Jul 24 '13 at 18:43
  • 1
    @MadScienceDreams I think the problem is overload resolution choosing the non-template `operator*` declared in the friend function declaration inside `Vector4` over the non-template function `operator*` declared&defined in "Vector4.inl" (those are two distinct functions). – dyp Jul 24 '13 at 18:45
  • @DyP oh man you are totally right. Because you have to predeclare friend classes, I didn't realize that friend functions declarations acts as function declarations. That's...well, weirdly inconsistent. [http://stackoverflow.com/questions/4492062/why-does-a-c-friend-class-need-a-forward-declaration-only-in-other-namespaces] – IdeaHat Jul 24 '13 at 18:52

3 Answers3

4

As stated in the comments, your friend function declaration

friend Vector4<T> operator*(const Vector4<T>& l, const Vector4<T>& r);

declares a non-template function in the global namespace. When you instantiate e.g. Vector4<int>, the function

Vector4<int> operator*(const Vector4<int>& l, const Vector4<int>& r)

is declared. Note that it's not a function template. (Also see [temp.friend])

Your Vector4.inl then declares and defines a function template

template<typename T>
Vector4<T> operator*(const Vector4<T>& l, const Vector4<T>& r)

i.e. an overload of the former function. In the expression a * b, overload resolution chooses the non-template operator* over the template version (see [over.match.best]/1). This results in a linker error, as the non-template function hasn't been defined.


As I've almost fooled myself, a short remark:

template<typename T>
Vector4<T> operator*(const Vector4<T>& l, const Vector4<T>& r);

As this operator is a free function (a non-member function), these two lines declare a function template, much like

template<typename T>
Vector4<T> wup();

On the other hand,

template<typename T>
Vector4<T> Vector4<T>::operator*(const Vector4<T>& r)
{ /* ... */ }

defines a member function (non-template) of a class template (Vector4).


One solution is to use forward-declarations and befriend only a specific specialization:

template<typename T>
class Vector4;

template<typename T>
Vector4<T> operator*(const Vector4<T>& l, const Vector4<T>& r);


template<typename T>
class Vector4
{
public:
    T X;
    T Y;
    T Z;
    T W;

    Vector4();
    Vector4(T X, T Y, T Z, T W);
    ~Vector4();

    // compiler knows of some function template `operator*`,
    // can name an specialization:
    // ~~~~~~~~~~~~~~~~~~~~~~~~vvv
    friend Vector4<T> operator*<T>(const Vector4<T>& l, const Vector4<T>& r);
};

template<typename T>
Vector4<T>::Vector4()
{
    X = 0;
    Y = 0;
    Z = 0;
    W = 0;
}

template<typename T>
Vector4<T>::Vector4(T X, T Y, T Z, T W)
{
   this->X = X;
   this->Y = Y;
   this->Z = Z;
   this->W = W;
}

template<typename T>
Vector4<T>::~Vector4()
{}

template<typename T>
Vector4<T> operator*(const Vector4<T>& l, const Vector4<T>& r)
{
     return(Vector4<T>(l.X * r.X, l.Y * r.Y, l.Z * r.Z, l.W * r.W));
}

int main()
{
    Vector4<float> a, b;
    a = a * b;
}

Another solution would be to friend the whole operator* template instead of a single specialization:

template<typename U>
friend Vector4<U> operator*(Vector4<U> const&, Vector4<U> const&);
dyp
  • 38,334
  • 13
  • 112
  • 177
  • Great answer with good explanation and examples. I forgot to add the argument list after the "operator*" in the class declaration, this solved my issue. – David Wright Nov 17 '17 at 09:44
1

I was able to change the code to nonfriended version:

  1. code inside class definition:

    Vector4<T> operator*(Vector4<T> const & r);
    
  2. code of operator*. It is changed to take only one parameter.

    template<typename T>
    Vector4<T> Vector4<T>::operator*(Vector4<T> const &r)
    {
         return(Vector4<T>(this->X * r.X, this->Y * r.Y, this->Z * r.Z, this->W * r.W));
    }
    
nio
  • 5,141
  • 2
  • 24
  • 35
  • This is a non-member function; `operator*` can be declared either as a member function (taking one parameter) or a non-member function (taking two parameters). Both "errors" you mention aren't actually errors. – dyp Jul 24 '13 at 18:53
  • my mistake, what is the purpose in using the friend operator? i've succeeded in C++11 in delaring one argument operator* without friending it. – nio Jul 24 '13 at 18:55
  • Here, the purpose is unclear. Normally you'd use the `friend` specifier if you want a function (or class) accessing `private` or `protected` members. It isn't necessary here. Note that using `friend` inside a class is a declaration and can declare new functions / classes, which leads to the error in the OP. – dyp Jul 24 '13 at 19:00
  • 1
    Yup, this is also a solution; though AFAIK, non-member functions are preferred for operators because of symmetry: for non-member (binary) operators, you can choose the type of the left hand side (like for `5 == Vector4()`). – dyp Jul 24 '13 at 19:02
0

First of all remove friend from your declaration of operator* in the .h file

it should look like this:

Vector4<T> operator*(const Vector4<T>& r);

Then in the .inl file operator* should look like this:

Vector4<T> Vector4<T>::operator*(const Vector4<T>& r){
     return(Vector4<T>(X * r.X, Y * r.Y, Z * r.Z, W * r.W));
}

The operand on the left hand side of the operator is what called this member function.

HopAlongPolly
  • 1,347
  • 1
  • 20
  • 48
  • The thing you want to change the friend declaration into is ill-formed. Typo? (like, only one parameter?) – dyp Jul 24 '13 at 19:08