By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
449,079 Members | 915 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 449,079 IT Pros & Developers. It's quick & easy.

multi-template parameter friend template functions

P: 12
I've got a container that looks like this:
Expand|Select|Wrap|Line Numbers
  1. template <class T>
  2. class Foo
  3. {
  4.  public:
  5.   Foo() : _data(), _status(1) { }
  6.   Foo(T) : _data(T), _status(0) { }
  7.   ~Foo() { }
  8.  
  9.   template<class U>
  10.   Foo<T>& operator=(const U& u) {_data=u; _status=0; return *this;}
  11.  
  12.  private:
  13.   T _data;
  14.   int _status;
  15. };
  16.  
  17.  
...and would like to be able to do things like this:

Expand|Select|Wrap|Line Numbers
  1. Foo<float> fooF(3.5);
  2. Foo<double> fooD1(4.7);
  3. Foo<double> fooD2=fooF+fooD1;
  4.  
"Easy," I think to myself, write a friend operator+:

Expand|Select|Wrap|Line Numbers
  1. //gcc typeof(T()+U()) results in an ICE... this is my work-around
  2. template <class T, class U>
  3. struct Promote
  4. {
  5.  static const T t;
  6.  static const U u;
  7.  typedef typeof(t+u) Result;
  8. };
  9. template <class T> Foo; //forward declaration of class Foo 
  10. template <class T, class U> Foo<typename Promote<T,U>::Result> operator+(const Foo<T>& lhs, const Foo<U>& rhs); //identify operator+ as template func
  11.  
  12. template <class T>
  13. class Foo
  14. {
  15.  public:
  16.   Foo() : _data(), _status(1) { }
  17.   Foo(T) : _data(T), _status(0) { }
  18.   ~Foo() { }
  19.  
  20.   template<class U>
  21.   Foo<T> operator=(const U& u) {_data=u; _status=0;}
  22.   template <class U>
  23.   Foo<typename Promote<T,U>::Result> operator+<>(const Foo<T>& lhs, const Foo<U>& rhs);
  24.  
  25.  private:
  26.   T _data;
  27.   int _status;
  28. };
  29.  
  30. //operator+() definition
  31. template <class T, class U> Foo<typename Promote<T,U>::Result> operator+(const Foo<T>& lhs, const Foo<U>& rhs)
  32. {
  33.  Foo<typeof(lhs._data+rhs._data)> result;
  34.  result._data=lhs._data+rhs._data;
  35.  result._status=lhs._status|rhs._status;
  36.  return result;
  37. }
  38.  
One of the problems I'm running into may be compiler related: in the class declaration of the friend function (line 25 above), I get an error:
Expand|Select|Wrap|Line Numbers
  1. error: invalid use of template-id `operator+<>' in declaration of primary template
  2.  
...which confuses me, because I thought this was the correct way to declare friend templates as per the C++ FAQ and the GCC FAQ. But, those examples didn't take multiple template parameters, so maybe something changes.

Another problem that I think I will run into is that I don't know if the friendship mechanism will allow Foo<T>._data and Foo<U>._data to be accessible in the operator+() function. That is my hope, even though Foo<float> and Foo<double> are distinct types. If not, how do I allow such a thing without using a public accessor?

Basically the purpose of this container is to couple a status with a POD. We need a way in our system to flag variables as invalid, and for that status to cascade to any variables that are results of calculations of that invalid variable. We'd still like to treat these variable containers like POD, though, except for special cases during comparisons and extraction of the actual data it contains (needs to be valid, or else it throws... hence public accessor can't be used in operators). The above class is a slight over-simplification, but illustrates the problem.

I'm not sure this is the best way to achieve what I want, or if it's even possible within C++, but I couldn't find any generic containers out there that would allow POD-style operators between containers of compatible PODs. I'm open to suggestions for alternate methods/containers/idioms!

My previous incarnation of this class basically had a byte array, an enumerated identifier that specified the POD type to emulate, a lot of hand-coded operators with switch() statements and conversion constructors. The run-time overhead started to get significant, so I'm trying to re-work as much of the type stuff into compile-time as possible.

OS: GNU/linux
Compiler: gcc 3.4.6

Thanks in advance for any insight!
Sep 20 '07 #1
Share this Question
Share on Google+
9 Replies


P: 12
I've got a container that looks like this:
Expand|Select|Wrap|Line Numbers
  1. template <class T>
  2. class Foo
  3. {
  4.  public:
  5.   Foo() : _data(), _status(1) { }
  6.   Foo(T) : _data(T), _status(0) { }
  7.   ~Foo() { }
  8.  
  9.   template<class U>
  10.   Foo<T>& operator=(const U& u) {_data=u; _status=0; return *this;}
  11.  
  12.  private:
  13.   T _data;
  14.   int _status;
  15. };
  16.  
  17.  
...and would like to be able to do things like this:

Expand|Select|Wrap|Line Numbers
  1. Foo<float> fooF(3.5);
  2. Foo<double> fooD1(4.7);
  3. Foo<double> fooD2=fooF+fooD1;
  4.  
"Easy," I think to myself, write a friend operator+:

Expand|Select|Wrap|Line Numbers
  1. //gcc typeof(T()+U()) results in an ICE... this is my work-around
  2. template <class T, class U>
  3. struct Promote
  4. {
  5.  static const T t;
  6.  static const U u;
  7.  typedef typeof(t+u) Result;
  8. };
  9. template <class T> Foo; //forward declaration of class Foo 
  10. template <class T, class U> Foo<typename Promote<T,U>::Result> operator+(const Foo<T>& lhs, const Foo<U>& rhs); //identify operator+ as template func
  11.  
  12. template <class T>
  13. class Foo
  14. {
  15.  public:
  16.   Foo() : _data(), _status(1) { }
  17.   Foo(T) : _data(T), _status(0) { }
  18.   ~Foo() { }
  19.  
  20.   template<class U>
  21.   Foo<T> operator=(const U& u) {_data=u; _status=0;}
  22.   template <class U>
  23.   Foo<typename Promote<T,U>::Result> operator+<>(const Foo<T>& lhs, const Foo<U>& rhs);
  24.  
  25.  private:
  26.   T _data;
  27.   int _status;
  28. };
  29.  
  30. //operator+() definition
  31. template <class T, class U> Foo<typename Promote<T,U>::Result> operator+(const Foo<T>& lhs, const Foo<U>& rhs)
  32. {
  33.  Foo<typeof(lhs._data+rhs._data)> result;
  34.  result._data=lhs._data+rhs._data;
  35.  result._status=lhs._status|rhs._status;
  36.  return result;
  37. }
  38.  
One of the problems I'm running into may be compiler related: in the class declaration of the friend function (line 25 above), I get an error:
Expand|Select|Wrap|Line Numbers
  1. error: invalid use of template-id `operator+<>' in declaration of primary template
  2.  
...which confuses me, because I thought this was the correct way to declare friend templates as per the C++ FAQ and the GCC FAQ. But, those examples didn't take multiple template parameters, so maybe something changes.

Another problem that I think I will run into is that I don't know if the friendship mechanism will allow Foo<T>._data and Foo<U>._data to be accessible in the operator+() function. That is my hope, even though Foo<float> and Foo<double> are distinct types. If not, how do I allow such a thing without using a public accessor?

Basically the purpose of this container is to couple a status with a POD. We need a way in our system to flag variables as invalid, and for that status to cascade to any variables that are results of calculations of that invalid variable. We'd still like to treat these variable containers like POD, though, except for special cases during comparisons and extraction of the actual data it contains (needs to be valid, or else it throws... hence public accessor can't be used in operators). The above class is a slight over-simplification, but illustrates the problem.

I'm not sure this is the best way to achieve what I want, or if it's even possible within C++, but I couldn't find any generic containers out there that would allow POD-style operators between containers of compatible PODs. I'm open to suggestions for alternate methods/containers/idioms!

My previous incarnation of this class basically had a byte array, an enumerated identifier that specified the POD type to emulate, a lot of hand-coded operators with switch() statements and conversion constructors. The run-time overhead started to get significant, so I'm trying to re-work as much of the type stuff into compile-time as possible.

OS: GNU/linux
Compiler: gcc 3.4.6

Thanks in advance for any insight!
OK, I found a way to skirt the friend function issue altogether. It's tricky, and I like it... ready :)?

Foo now looks like this:

Expand|Select|Wrap|Line Numbers
  1. template <class T>
  2. class Foo
  3. {
  4.  public:
  5.   Foo() : _data(), _status(1) { }
  6.   Foo(T) : _data(T, int status=0), _status(status) { } //skirts an issue accessing protected members in the result temp in operator+
  7.   ~Foo() { }
  8.  
  9.   template<class U>
  10.   Foo<T>& operator=(const U& u) {_data=u; _status=0; return *this;}
  11.  
  12.  protected:
  13.   T _data;
  14.   int _status;
  15. };
  16.  
No friend declarations. Global operator+ overload looks like this:
Expand|Select|Wrap|Line Numbers
  1. template<class T, class U>
  2. Foo<typename Promote<T,U>::Result> operator+(const Foo<T>& lhs, const Foo<U>& rhs)
  3. {
  4.  class FooSum : public Foo<T>, public Foo<U>
  5.  {
  6.   FooSum(const Foo<T>& t, const Foo<U>& u) : Foo<T>(t), Foo<U>(u) { }
  7.   Foo<typename Promote<T,U>::Result> GetSum()
  8.   {
  9.    return Foo<typename Promote<T,U>::Result> (Foo<T>::_data+Foo<U>::_data, Foo<T>::_status|Foo<U>::_status);
  10.   }
  11.  };
  12.  FooSum fs(lhs,rhs);
  13.  return fs.GetSum();
  14. }
  15.  
Similar for the case where T=U, only with a single inheritance accessor class to avoid inheriting off of the same base twice.

So, that fixes my Foo<float>+Foo<double> case. What it does not fix, however, is my Foo<T>+POD case. In fact, declaring something like this:

Expand|Select|Wrap|Line Numbers
  1. template <class T, class U>
  2. Foo<typename Promote<T,U>::Result> operator+(const Foo<T>& lhs, const U rhs);
  3.  
BREAKS the Foo<T>+Foo<U> case that I had just solved, because it tries to instantiate that function with U=Foo<U> instead of instantiating the one where Foo<U> is an explicit parameter. This is, perhaps, a problem for another thread, though, as it no longer concerns the title...
Sep 20 '07 #2

weaknessforcats
Expert Mod 5K+
P: 9,197
Skirting a problem doesn't solve it.

First: This is not how you declare a firend function:
template <class T>
class Foo
{

template <class T, class U>
Foo<typename Promote<T,U>::Result> operator+<>(const Foo<T>& lhs, const Foo<U>& rhs);

etc...
You want to overload operator+() so you use parentheses and not angle brackets. Plus you need to add the friend declaration. This is correct:
Expand|Select|Wrap|Line Numbers
  1. template <class T, U>
  2. class Foo
  3. {
  4.  
  5. template <class T, class U>
  6. friend Foo<typename Promote<T,U>::Result> operator+(const Foo<T>& lhs, const Foo<U>& rhs);
  7.  
  8. etc...
  9.  
Unfortunately, this is a template so you can't use a function prototype. So this won't compile.

Now you need to make a decision: Does this operator+() template work for all types or just one set of types? If the answer is yes, then you can use an expanded template:
Expand|Select|Wrap|Line Numbers
  1. template <class T,U>
  2. class Foo
  3. {
  4.  
  5. friend Foo<Promote<int,double>::Result> operator+(const Foo<int>& lhs, const Foo<double>& rhs);
  6.  
This is called a bounded friend template.

If you decide that all types will work, then you need to define the friend inside the class template:
Expand|Select|Wrap|Line Numbers
  1. template <class T,U>
  2. class Foo
  3. {
  4.  
  5. template <class T, class U>
  6. friend Foo<typename Promote<T,U>::Result> operator+(const Foo<T>& lhs, const Foo<U>& rhs)
  7. {
  8.  Foo<typeof(lhs._data+rhs._data)> result;
  9.  result._data=lhs._data+rhs._data;
  10.  result._status=lhs._status|rhs._status;
  11.  return result;
  12. }
  13.  
  14. etc...
  15.  
This is called an unbounded friend template.

Check out C++ Primer Plus 5th ed. by Stephen Prata page pp 773-777.
Sep 22 '07 #3

P: 12
Skirting a problem doesn't solve it.

First: This is not how you declare a firend function:


You want to overload operator+() so you use parentheses and not angle brackets. Plus you need to add the friend declaration. This is correct:
Expand|Select|Wrap|Line Numbers
  1. template <class T, U>
  2. class Foo
  3. {
  4.  
  5. template <class T, class U>
  6. friend Foo<typename Promote<T,U>::Result> operator+(const Foo<T>& lhs, const Foo<U>& rhs);
  7.  
  8. etc...
  9.  
Unfortunately, this is a template so you can't use a function prototype. So this won't compile.

Now you need to make a decision: Does this operator+() template work for all types or just one set of types? If the answer is yes, then you can use an expanded template:
Expand|Select|Wrap|Line Numbers
  1. template <class T,U>
  2. class Foo
  3. {
  4.  
  5. friend Foo<Promote<int,double>::Result> operator+(const Foo<int>& lhs, const Foo<double>& rhs);
  6.  
This is called a bounded friend template.

If you decide that all types will work, then you need to define the friend inside the class template:
Expand|Select|Wrap|Line Numbers
  1. template <class T,U>
  2. class Foo
  3. {
  4.  
  5. template <class T, class U>
  6. friend Foo<typename Promote<T,U>::Result> operator+(const Foo<T>& lhs, const Foo<U>& rhs)
  7. {
  8.  Foo<typeof(lhs._data+rhs._data)> result;
  9.  result._data=lhs._data+rhs._data;
  10.  result._status=lhs._status|rhs._status;
  11.  return result;
  12. }
  13.  
  14. etc...
  15.  
This is called an unbounded friend template.

Check out C++ Primer Plus 5th ed. by Stephen Prata page pp 773-777.
Whoops, I knew I should have cut&pasted the code, but it's sitting on an embedded target... I actually did have the friend keyword in the declaration. Thanks for pointing that out.

As far as the empty <> goes, I was following the directions found here for making template friend functions:
http://www.parashift.com/c++-faq-lit...html#faq-35.16
...since I was getting the linker errors mentioned therein.

I originally tried the unbounded friend example that you wrote above, but was informed by my compiler that Foo<U>'s members were private in that context. I'm assuming this is because the compiler assumed friend operator+ was declared a friend of Foo<T>, and not Foo<U>. If I invoked that operator with T=U, all was well, but instantions of Foo with different types would result in error. Is this incorrect behavior on my compiler's part? Is there a way to generalize a friend so that it can access two different class instantiations within the same function body?

Thanks for your response!
Sep 25 '07 #4

weaknessforcats
Expert Mod 5K+
P: 9,197
Is there a way to generalize a friend so that it can access two different class instantiations within the same function body?
Yes. But these are the bounded friends. You must know the types of the instantiations:

Expand|Select|Wrap|Line Numbers
  1. template <class T,U>
  2. class Foo
  3. {
  4.  
  5. friend Foo<Promote<int,double>::Result> operator+(const Foo<int>& lhs,  
  6. friend Foo<Promote<string,int>::Result> operator+(const Foo<string>& lhs, const Foo<int>& rhs);
  7. etc...
  8.  
  9.  
I'm just making up types, so please excuse me.

The tempate<> syntax is for explicit specialization.

This syntax tells the compiler to not use the template for the types shown because you have already written the function explicitly.
Sep 26 '07 #5

P: 12
Yes. But these are the bounded friends. You must know the types of the instantiations:

Expand|Select|Wrap|Line Numbers
  1. template <class T,U>
  2. class Foo
  3. {
  4.  
  5. friend Foo<Promote<int,double>::Result> operator+(const Foo<int>& lhs,  
  6. friend Foo<Promote<string,int>::Result> operator+(const Foo<string>& lhs, const Foo<int>& rhs);
  7. etc...
  8.  
  9.  
That certainly works, although it seems rather tedious, and sort of defeats the purpose of templating Foo. I guess what I meant was, is there a way here to indicate to the compiler that given template parameters T and U, operator+ is friend to both Foo<T> and Foo<U>, in a single declaration? I think the answer is no, and as I further ponder how the compiler architects would accomplish such a thing (since a specialization of Foo might not even have the same data members), I become more convinced that this is so.

The tempate<> syntax is for explicit specialization.

This syntax tells the compiler to not use the template for the types shown because you have already written the function explicitly.
I understand the use of the empty angle brackets in that context. I was surprised to see them used this way, myself, but according to the C++ FAQ, the GCC FAQ, and GCC's handy (and specific!) compiler warnings, that is the correct syntax for declaring a friend function to a template class, that is itself a template function. Otherwise, the compiler won't generate the code for the friend function (because it doesn't think it is a template), and you are stuck with a linker error. Seems a bit backwards from the normal use of the <> with templates. Note, however, that defining the friend function inside the class gets rid of the problem, as you showed above in the unbounded template example. Although, I still received an error about the right-hand-side's data members being private, so I'm still out of luck there.
Sep 28 '07 #6

weaknessforcats
Expert Mod 5K+
P: 9,197
I finally tried to compile your code and got various errors. The big one is that Foo requires two types so Foo<T> won't compile.

I had to:
Expand|Select|Wrap|Line Numbers
  1. template <class T,class U>
  2. class Foo
  3. {
  4. public:   
  5.  
  6.    template <class T, class U>
  7.    friend Foo<Promote<T, U>, T > operator+(const Foo<T,U>& lhs,
  8.                                                                  const    Foo<T,U>& rhs)
  9.    {
  10.  
  11.    }
  12. };
  13.  
What am I missing?
Sep 28 '07 #7

P: 12
I finally tried to compile your code and got various errors. The big one is that Foo requires two types so Foo<T> won't compile.

I had to:
Expand|Select|Wrap|Line Numbers
  1. template <class T,class U>
  2. class Foo
  3. {
  4. public:   
  5.  
  6.    template <class T, class U>
  7.    friend Foo<Promote<T, U>, T > operator+(const Foo<T,U>& lhs,
  8.                                                                  const    Foo<T,U>& rhs)
  9.    {
  10.  
  11.    }
  12. };
  13.  
What am I missing?
Ah! Check back to my original post. Foo actually only takes one template parameter, T, being the type contained. The operator+, however, will have two template parameters when adding two Foos templated on different types.

This compiles and works for me (gcc 3.4.6):
Expand|Select|Wrap|Line Numbers
  1. #include <iostream>
  2. #include <string>
  3. using namespace std;
  4.  
  5. template <class T, class U>
  6. struct Promote
  7. {
  8.     const static T t;
  9.     const static U u;
  10.     typedef typeof(t+u) Result;
  11. };
  12.  
  13. template <class T>
  14. class Foo 
  15. {
  16.     public:
  17.     typedef T PODType;
  18.  
  19.     Foo() : _data(), _status(1) { }
  20.     Foo(T data, int status) : _data(data), _status(status) { }
  21.     Foo(T data) : _data(data), _status(0) { }
  22.     ~Foo() { }
  23.  
  24.     T GetData() {if(_status) throw; return _data;}
  25.  
  26.     protected:
  27.     T _data;
  28.     int _status;
  29. };
  30.  
  31. template <class T, class U>
  32. Foo<typename Promote<T,U>::Result> operator+ (const Foo<T>& lhs, const Foo<U>& rhs)
  33. {
  34.     class FooSum : public Foo<T>, public Foo<U>
  35.     {
  36.     public:
  37.         FooSum(const Foo<T>& t, const Foo<U>& u)
  38.         : Foo<T>(t), Foo<U>(u)
  39.         { }
  40.         Foo<typename Promote<T,U>::Result> GetSum()
  41.         {
  42.                 return Foo<typename Promote<T,U>::Result> 
  43.                (Foo<T>::_data+Foo<U>::_data,
  44.                 Foo<T>::_status|Foo<U>::_status);
  45.         }
  46.     };
  47.  
  48.     FooSum fs(lhs,rhs);
  49.     return fs.GetSum();
  50. }
  51.  
  52. template <class T>
  53. Foo<T> operator+ (const Foo<T>& lhs, const Foo<T>& rhs)
  54. {
  55.     class FooAccessor : public Foo<T>
  56.     {
  57.     public:
  58.         FooAccessor(const Foo<T>& t)
  59.         : Foo<T>(t)
  60.         { }
  61.         T GetData() {return Foo<T>::_data;}
  62.         int GetStatus() {return Foo<T>::_status;}
  63.     };
  64.  
  65.     FooAccessor faLHS(lhs);
  66.     FooAccessor faRHS(rhs);
  67.  
  68.     return Foo<T>(faLHS.GetData()+faRHS.GetData(),faLHS.GetStatus()|faRHS.GetStatus());
  69. }
  70.  
  71. int main()
  72. {
  73.     Foo<float> fooFloat1(10.3);
  74.     Foo<float> fooFloat2(27.4);
  75.     Foo<double> fooDouble1(43.2);
  76.  
  77.     float f = (fooFloat1+fooFloat2).GetData();
  78.  
  79.     cout << "f is:" << f << endl;
  80.  
  81.     f = (fooFloat1 + fooDouble1 + fooFloat2).GetData();
  82.     cout << "f is:" << f << endl;
  83.  
  84.     return EXIT_SUCCESS;
  85. }
  86.  
Obvious things I don't like are having to construct extra objects inside operator+, but I could not get the friend functions to work. I am sure there is a better way, but my template kung-fu just isn't that strong yet :).

I really appreciate your time and patience exploring this problem; I've exhausted the human resources here as well as the books Modern C++ Design by Andrei Alexandrescu and C++ Common Knowledge by Stephen Dewhurst looking for a way to implement what I thought would be a simple container. I have either way underestimated the problem or way overestimated my intelligence :).
Sep 29 '07 #8

weaknessforcats
Expert Mod 5K+
P: 9,197
Ah! Check back to my original post. Foo actually only takes one template parameter, T,
I missed that. Obviously, I introduced an error and confused myself. Sorry.
Sep 29 '07 #9

P: 12
Just in case anybody has a similar situation or was curious, this can be done with friend functions.

With Foo declared as above, add the friend declaration:

Expand|Select|Wrap|Line Numbers
  1. template <class T>
  2. class Foo
  3. {
  4. public:
  5.   ...
  6.   typedef T ContainedType
  7.   ...
  8.   template <class U, class V>
  9.   friend 
  10.   Foo<typename Promote<typename U::ContainedType,typename V::ContainedType>::Result>
  11.   operator+(const U& lhs, const V& rhs);
  12.   ...
  13. };
  14.  
  15. template <class U, class V>
  16. Foo<typename Promote<typename U::ContainedType,typename V::ContainedType>::Result>
  17. operator+(const U& lhs, const V& rhs)
  18. {
  19.   return Foo<typename Promote<typename U::ContainedType,typename V::ContainedType>::Result>(lhs._data+rhs._data,lhs._status|rhs._status);
  20. }

That seems to work fine for adding Foo<T> to Foo<U>.

The trick was to use template parameters to represent whole containers themselves, not the types contained, I suppose. Note that summing intrinsics with containers will require overloads of operator+ and some manipulation of the overload set to work properly (I used boost::lazy_enable_if to help the compiler along, otherwise Promote busts).
Oct 11 '07 #10

Post your reply

Sign in to post your reply or Sign up for a free account.