2

Can somebody explain me what I am doing wrong? This is teh error I get from the compiler.

Many Thanks

1>------ Build started: Project: Ch16, Configuration: Release Win32 ------
1>  p643_inclusion.cpp
1>  p643_inclusion_main.cpp
1>  p643_print.cpp
1>p643_print.cpp(5): error C2065: 'T1' : undeclared identifier
1>p643_print.cpp(5): error C2065: 'T2' : undeclared identifier
1>p643_print.cpp(6): warning C4552: '<<' : operator has no effect; expected operator with side-effect
1>p643_print.cpp(7): warning C4552: '<<' : operator has no effect; expected operator with side-effect
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

p643_inclusion.h

#ifndef P643H
#define P643H

template< class T1, class T2> class Car { 

    friend void  print (const Car<T1, T2> &c1);
    private:
        T1 Wheels;
        T2 DriversName;
    public:
        Car(): Wheels(4), DriversName("None") {}
        Car(T1, T2);
};



template <class T1, class T2> class Driver {
    private:
        T1 Name;
        T2 Surname;
    public:
        Driver(): Name("None"), Surname("None") {}
};


#include "p643_inclusion.cpp"

#endif

p643_inclusion.cpp

# ifndef P643CC
#define P643CC

#include <iostream>
#include <string>
using std::string;
using std::cout;
using std::endl;
#include "p643_inclusion.h"

template<class T1, class T2>
Car<T1, T2>::Car(T1 w, T2 d) {
    Wheels = w;
    DriversName = d;
}

#endif

p643_print.cpp

#include "p643_inclusion.h"
template< class T1, class T2> class Car;

void  print (const Car<T1, T2> &c1) {
    cout << c1.Wheels << endl;
    cout << c1.DriversName << endl;
    }

main

#include "p643_inclusion.h"
#include<iostream>
#include<string>
using namespace std;


int main()
{

    Car<int, string> myCar;
    Driver<string, string> myDriver;

    print(myCar);

    return 0;
}
RandomCPlusPlus
  • 359
  • 1
  • 5
  • 12
  • include the file *p643_print.cpp* in *p643_inclusion.h* as exactly in the way you have included `p643_inclusion.cpp`. – Nawaz Apr 05 '11 at 16:45

2 Answers2

4

Your function is not actually a non-template function.

void  print (const Car<T1, T2> &c1) {
    cout << c1.Wheels << endl;
    cout << c1.DriversName << endl;
}

This is wrong. Can you ask yourself what exactly is T1? and T2?


You should implement this as:

template<class T1, class T2>
void  print (const Car<T1, T2> &c1) {
    cout << c1.Wheels << endl;
    cout << c1.DriversName << endl;
}

And you should make it friend as:

template< class T1, class T2> class Car { 

    //choose different name for type params, because enclosing class 
    //already using T1, and T2
    template<class U, class V>
    friend void  print (const Car<U, V> &c1);

    //...
metamorphosis
  • 1,972
  • 16
  • 25
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • Fair point. Thanks for this and for the speed you replied! So essentially non template functions cannot take any template parameters. Only template functions can do so. I didnt know this - I mean I knew it but didnt think straight... – RandomCPlusPlus Apr 05 '11 at 16:08
  • 1
    @RandomCPlusPlus: Yes. template implies template parameters! – Nawaz Apr 05 '11 at 16:10
  • Hi again, your comment was very helpful and at least I realised one of my mistakes. There must be another one thoiugh. I followed your advice but now I get this 1> p643_inclusion_main.cpp 1>p643_inclusion_main.cpp(13): error C3861: 'print': identifier not found Thanks again – RandomCPlusPlus Apr 05 '11 at 16:26
  • @RandomCPlusPlus: Post the new update code so what I can see it myself. – Nawaz Apr 05 '11 at 16:30
  • This changes the semantics, and opens the class to potentially many classes you don't want. A template class can have all shorts of function friends (or at least 3): a non-templated function, a function template or a particular instantiation of a templated function (plus other mix and match, and class friends...) This solution is the most open: any instantiation (including specializations!) of the template are befriended. I would advice against this solution. Take a look at this: http://stackoverflow.com/questions/4660123/overloading-friend-operator-for-template-class/4661372#4661372 – David Rodríguez - dribeas Apr 05 '11 at 16:37
  • @Random, Nawaz: This is incorrect. Non-template friend functions can be templated, the requirement is that the definition appear inside the `class { }` scope of the friending class. (I just answered another question like this, too…) – Potatoswatter Apr 05 '11 at 16:39
  • @ Potatoswatter, can you pelase link your replys to the other question? Thanks – RandomCPlusPlus Apr 05 '11 at 16:43
  • @RandomCPlusPlus: include the file *p643_print.cpp* in *p643_inclusion.h* as exactly in the way you have included `p643_inclusion.cpp`. – Nawaz Apr 05 '11 at 16:47
2

Mixing templates and friendship is not always as simple as it might seem. My advice is that you define the befriended function in the class definition, and then your problem will basically go away:

template <typename T1, typename T2>
struct test {
   friend void print( test const & t ) { ... };
};

For each instantiation of the template test, it will declare and define a (non templated) free function that takes a test object instantiated with the same template arguments that triggered the instantiation of the template.

Other options available (I would stay clear of these if possible):

You can make print be a template and declare that template a friend of your class template (the whole template):

template <typename T1, typename T2>
struct test {
   template <typename U, typename V>
   friend void foo( test<U,V> const & ); // befriend template, all instantiations
};
template <typename T1, typename T2>
void foo( test<X,Y> const & x ) {...}

This opens your internals to all of the potential instantiations of the template, including possible specializations, and you might not want to do that. If you want to befriend only a particular instantiation of that template, you can do so, but it becomes more cumbersome:

template <typename T1, typename T2> struct test;  // forward declaration

template <typename T1, typename T2>
void foo( test<T1,T2> const & );                  // forward declaration

template <typename T1, typename T2>
struct test {
   friend void foo<T1,T2>( test<T1,T2> const & ); // befriend specific instantiation
};

template <typename T1, typename T2>
void foo( test<T1,T2> const & x ) { ... }         // implement

For a further explanation, you can look at the answer here

Community
  • 1
  • 1
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489