1

I'm trying to make the multiplication operator a friend of a template class named TVector3. I have read that I could make a forward declaration of the friend function before declaring it in the class declaration however my attempts of doing so were futile. I know I could simply define the friend function instead of declaring it, but I want it to work with forward declaration technique.

In particular I was trying to implement this solution for my case. I found this post too were David Rodriguez gives a solution (the third version) but I don't know what I'm doing wrong.

I compile using 'g++ template.cpp tempmain.cpp a' and The compiler (g++) gives the following error:

undefined reference to 'ray::TVector3 ray::operator*(float, ray::TVector3 const&)'

for the following code:

template.h:

#ifndef TVECTOR_H
#define TVECTOR_H

#include <cmath>

namespace ray
{
    //forward declarations
    template <class T> class TVector3;
    template <class T> TVector3<T> operator* (T, const TVector3<T> &);

    template <class T>
    class TVector3 {

        public:
            union {
                struct {
                    T x, y, z;
                };
                T xyz[3];
            };

        //function needed
        friend TVector3<T> operator*<T> (T, const TVector3<T> &);
    };

}

#endif

template.cpp:

#include "template.h"

using namespace ray;

//friend operator function
template<class T> TVector3<T> operator * (T f, const TVector3<T> &v)
{
    return TVector3<T>(f * v.x, f * v.y, f * v.z);
}

//instantiate template of type float
template class TVector3<float>;

tempmain.cpp:

#include <iostream>
#include "template.h"

int main()
{
    ray::TVector3<float> v1, v2;

    v2.x = 3;
    v1 = 4.0f * v2; //this reports as undefined

    std::cout << v1.x << "\n";

    return 0;
}

That is the full source code. What am I doing wrong?

Community
  • 1
  • 1
Kevin
  • 13
  • 4

2 Answers2

4

As a general rule of thumb, templates should be defined in the header. If you define them in the cpp file, you will need to manually instantiate the template. You are doing it for the class template, but you are not instantiating the operator* template.

template TVector3<T> operator*<T> (T, const TVector3<T> &);

Additionally, I would recommend that instead of using a templated operator* you used a non-template free function by declaring and defining it inside the class template:

template <class T>
class TVector3 {
//function needed
    friend TVector3<T> operator*(T f, const TVector3<T> & v) {
       return TVector3<T>(f * v.x, f * v.y, f * v.z);
    }
};

This has the advantages of free functions (it allows conversions on the first argument), and of templates (it will generate the free function on demand --i.e. no need to provide all the implementations manually) as well as limited visibility (it will only be accessible for lookup through ADL)

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • In my case, how would I instantiate the operator* template? I'm assuming I would need to do it in the cpp file. I am trying to do it that way as an exercise. Also I have another question, wouldn't defining template classes in the header bloat the executable when the header is included in other cpp source files? Thanks for the suggestions btw and for your prompt reply :) – Kevin Aug 29 '12 at 22:57
  • @Kevin: The code to instantiate the template is in the answer, the first line. Note that this is a feature rarely used so it might not compile (tell me if so), but it should be correct. Regarding code bloat, explicit instantiations of templates are more prone to code bloat. When you explicitly instantiate a template all of the member functions are instantiated with it (and friends defined inside the class template). On the other end, with implicit instantiations (all template definitions in the header), the compiler will generate functions on demand, so the executable will contain less code. – David Rodríguez - dribeas Aug 30 '12 at 00:02
  • @Kevin: ... although, because the definitions are in the header, it multiple translation units *use* (*odr-use*) the same member functions, the compiler will generate code in all translation units and it will be up to the linker to discard all but one definition. But after the linker completes, only one copy of only each used function will be in the executable. The con side is that compile times might be longer (the same code will be generated multiple times only to be discarded later). But that is seldom too much of an issue. – David Rodríguez - dribeas Aug 30 '12 at 00:06
  • Thanks it works now, I needed to specify the namespace as @BigBoss suggested. Also, thanks for the information regarding code bloating. – Kevin Aug 30 '12 at 09:01
  • @Kevin: Oh, yes, the namespace... I avoid `using namespace` always (and I recommend it). Just open the namespace again in the cpp file and add the code there. It will be less confusing than having to remember when or when not you still need to qualify with the namespace. – David Rodríguez - dribeas Aug 30 '12 at 13:16
0

you should explicitly instantiate operator* and it work like this

using namespace ray;

//friend operator function
template<class T> TVector3<T> ray::operator * (T f, const TVector3<T> &v)
{
    return TVector3<T>(f * v.x, f * v.y, f * v.z);
}

//instantiate template of type float
template class TVector3<float>;
template TVector3<float> ray::operator*<float>( float, TVector3<float> const& );

Remember that if you write operator* in place of ray::operator* compiler have no way to know you mean ray::operator* and you are not declaring a new operator in global namespace!

and don't forget to define TVector3( T x_, T y_, T z_ ) :)

BigBoss
  • 6,904
  • 2
  • 23
  • 38
  • Thanks specifying the namespace worked just fine :) I have one other question. How would I declare it in global namespace? I tried using (::operator*) in the header file but it wouldn't work. – Kevin Aug 30 '12 at 08:54