简体   繁体   中英

how do I specialize a bound template friend function to a template class?

I am trying to overload the output stream operator (<<) for a templated stack class I created (as a programming assignment). I am using the normal paradigm of declaring an friend operator<< function to the stack class. I followed Prata's example (C++ primer plus) of "bound template friend functions to a template class". The code compiles and executes, but doesn't give the desired result because the templated operator<< function is attempting to overload every occurrence of the output stream operator in main, when I only want it to overload the output stream operators for the Stack class. So I think that I need to specialize the overloaded operator<< function.

I constructed a simpler code example to demonstrate the problem. Please note that rather than using an overloaded operator<< in the example, I use a function called "oper()". The code follows (attached too), The problem is that oper() works for both the Bigger class (which I want) AND the BIG class (which I dont want). Can you show me how to specialize the templated oper() function?

    // Compiler:   gcc
// Purpose:    test templates. test bound template friend fns.
#include <iostream>
using namespace std;

// declare template function
template <typename T> std::ostream & oper (std::ostream &os, const T &);

template <typename T> class Bigger {
    T value;
public:
    Bigger(T init) {value = init;}
    // declare bound template friend functions
    friend std::ostream &oper<> (std::ostream &os, const Bigger<T> &);
};

class Bad { // but oper() can work with this class too!
    int i;
public:
    int value;
    Bad(): i(1),value(2) {};
};

// define full template functions
// want to specialize this function so that it only works with class Bigger!
template <typename T> std::ostream &oper (std::ostream &os, const T & big) {
    os << big.value;
    return os;
}

int main (int argc, char * argv[]) {
    Bigger <float> bf {3.1};
    // this is desired behavior. oper() acts as a friend to Bigger.
    cout << "oper bf: "; oper(cout,bf); cout<<endl;
    Bad bad;
    // this is undesired behavior. template is too loose allowing oper to work for Bad!!!
    cout << "oper bad: ";oper(cout,bad);cout<<endl;

    return 0;
}

how do I specialize a bound template friend function to a template class?

Simple answer you cannot. Function templates can only be fully specialized, but what you are asking is to provide something alike a partial specialization of class templates. The closest thing would be to provide a different base template (see Petr Budnik answer) rather than specializing.... but I would not do that either.

When defining friend operators (or friend functions in general) of a template, I prefer not to use templates, but define a single non-template operator as the friend of the template. This can be achieved in the language by providing the definition of the friend function inside the class definition:

template <typename T>
class MyTemplate {
   int data;
//...
   friend std::ostream operator<<(std::ostream& os, MyTemplate const& t) {
       os << t.data;
       return os;
   }
};

There are a couple of advantages to this approach, the first of which is that it allows to provide a generic implementation for a non-template function, and thus avoids all issues that arise with function template specializations (note: avoid specializing function templates!) and multiple base function templates that define overloads, and what not. At the same time, it creates those functions in a context where they can only be found through ADL lookup, which means that it won't pollute other contexts and will thus simplify error messages (or at least not complicate them further).

If you forward-declare Bigger then you can restrict oper to work only on specializations of Bigger :

// declare template function
template <typename T> class Bigger;
template <typename T> std::ostream & oper (std::ostream &os, const Bigger<T> &);

template <typename T> class Bigger {
    T value;
public:
    Bigger(T init) {value = init;}
    // declare bound template friend functions
    friend std::ostream &oper<> (std::ostream &os, const Bigger &);
};

// define full template functions
template <typename T> std::ostream &oper (std::ostream &os, const Bigger<T> &big) {
    os << big.value;
    return os;
}

This allows you to use a template definition for oper but only make the single specialization oper<T> be a friend to Bigger<T> . Note that the syntax is much more complicated than David's answer - which is how I also do this in practice.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM