简体   繁体   中英

(Was originally two questions, but one is now answered) How to use enable_if to declare template class specializations?

Unfortunately the behavior I want to figure out is relevant to multiple compilation units. So I cannot provide a simple example to run in ideone/coliru. But I did write up a minimal code example.

Note this code as written here does compile and work. My questions are about why certain changes made to it don't work, and how to properly structure this code to meet my goals.

Update : The secondary question has been answered by @dyp. Only the question in the title remains. Thanks!

header:

// header 

#include <iostream>
#include <sstream>
using namespace std;

struct stream {
    std::stringstream ss;
    string str() const;
};
class htmlstream : public stream {
};
class jsonstream : public stream {
};

template <typename STREAM>
class writer {
    STREAM s;
public: 
    template <typename T> friend writer<STREAM>& operator<<(writer<STREAM>&, const T&);
    int write();
};

template <typename T> htmlstream& operator<<(htmlstream& hs, const T& t) {
    hs.ss << "<div>" << t << "</div>";
    return hs;
}
template <typename T> jsonstream& operator<<(jsonstream& js, const T& t) {
    js.ss << "{ \"t\": " << t << " }";
    return hs;
}

// this solves undefined reference to writer<htmlstream>& op<< <int>() linker error
// I want to write an operator<< that is generic both in the parameter of writer and in
// the type T
template <typename T> writer<htmlstream>& operator<<(writer<htmlstream>& w, const T& t) {
    w.s << t;
    return w;
}

#if 0
// why doesn't this work? (I am just missing something simple I think)
template <typename T, typename STREAM> writer<STREAM>& operator<<(writer<STREAM>& w, const T& t) {
    w.s << t;
    return w;
}
#endif

implementation cpp (includes header):

string stream::str() const {
    return ss.str();
}

template <typename STREAM> int writer<STREAM>::write() {
    cout << "writing: " << s.str() << endl;
    return 0;
}

// instantiation of writer -- I need to have this otherwise linker error. I want this to be declared generally so I can cover all descendants of stream in one statement.
template class writer<htmlstream>;

calling code cpp (includes header):

int main() {
    writer<htmlstream> hw;
    hw << 4;

    cout << hw.write() << endl;
    return 0;
}

Question 1:

  • As you can see I have multiple derived streams ( htmlstream and jsonstream ). I don't want to have to write template class writer<htmlstream>; as well as template class writer<jsonstream>; . I would like to use enable_if and is_base_of to achieve this. How? Also, why does compilation fail if I write template<> class writer<htmlstream> instead?

Question 2 (answered in comments) :

  • Notice the #if 0 commented template function that I attempted to write, which compiles but fails to match anything. I want to be able to specify an operator<< that works on all writer parameterized types as well as all right-hand-side argument types. Is this possible? How is it written? (What keywords do I use to Google this?)

(mini-question) I also would like to lift things out of the header into the implementation file if possible. But it seems like this cannot be done for many of the templates. Maybe I just need to try declaring these templates extern in the header?

Honesty, I don't quite get what you're asking, why you need derived streams and what (useful) a writer does on top of a stream , but if I get what you want to do right, here is a simple way ( live example ):

template<typename tag>
struct stream {
    std::stringstream ss;
    std::string str() const { return ss.str(); };
};

struct html { };
struct json { };

template<typename T>
stream<html>& operator<<(stream<html>& hs, const T& t) {
    hs.ss << "<div>" << t << "</div>";
    return hs;
}

template<typename T>
stream<json>& operator<<(stream<json>& js, const T& t) {
    js.ss << "{ \"t\": " << t << " }";
    return js;
}

int main() {
    stream<html> hs;
    stream<json> js;
    hs << 4;
    js << 4;
    cout << hs.str() << endl;
    cout << js.str() << endl;
}

Output:

<div>4</div>
{ "t": 4 }

Just keep all template code in a header without separating definitions.

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