简体   繁体   中英

Could I have an explanation of a few lines in the following code?

I would like to understand how the following code works. It can basically be used to sort of "Debug" C++ containers, but I'm having some trouble understanding lines 3, 4, 12 and 13 and what they do. It would be great if I could receive a brief explanation. Thanks.

template <typename T> class range { public: T begin, end; };
template <typename T> range <T> make_range (const T& b, const T& e) { return range <T> {b, e}; }
template <typename T> auto dec (T* x) -> decltype(std::cerr << *x, 0); // 3
template <typename T> char dec (...);  // 4
class view {
private:
    std::ostream& stream;
public:
     view (std::ostream&);
    ~view ();
#ifdef Z_LOCAL
    template <typename T> typename std::enable_if <sizeof dec <T> (0) != 1, view&>::type operator << (const T&);  // 12
    template <typename T> typename std::enable_if <sizeof dec <T> (0) == 1, view&>::type operator << (const T&);  // 13
    template <typename T> view& operator << (const range <T>&);
    template <typename A, typename B> view& operator << (const std::pair <A, B>&);
#else
    template <typename T> view& operator << (const T&);
#endif
};

view::view (std::ostream& os = std::cerr) : stream (os) { }

view::~view () { stream << std::endl; }

#ifdef Z_LOCAL
template <typename T> typename std::enable_if <sizeof dec <T> (0) != 1, view&>::type view::operator << (const T& t)
{ stream << std::boolalpha << t; return *this; }

template <typename T> typename std::enable_if <sizeof dec <T> (0) == 1, view&>::type view::operator << (const T& t)
{ return *this << make_range(begin(t), end(t)); }

template <typename T> view& view::operator << (const range <T>& r)
{ stream << "["; for (auto i = r.begin, j = i; i != r.end; ++i) *this << *i << (++j == r.end ? "]" : ", "); return *this; }

template <typename A, typename B> view& view::operator << (const std::pair <A, B>& p)
{ stream << '(' << p.first << ", " << p.second << ')'; return *this; }
#else
template <typename T> view& view::operator << (const T&)
{ return *this; }
#endif

#define print(x) " [" << #x << ": " << x << "] "
view debug (std::cerr);

The following declarations are using a technique that relies on SFINAE (substitution failure is not an error):

// #3 : works only if  (std::cerr << *x) works
template <typename T> auto dec (T* x) -> decltype(std::cerr << *x, 0);

// #4 : works for everything, but only if #3 fails
template <typename T> char dec (...); 

Consider some type T such as double . Then the following code, where x is a double* :

std::cerr << *x;

would compile just fine, which means that this code:

decltype(std::cerr << *x, 0);

is well formed, and evaluates to int . The compiler will then pick #3 when T is a type that can be used like this.

On the other hand, if T is something like a vector<double> , then the decltype is not well formed (since you can't stream a vector to cerr ), and so the compiler has no choice but to pick #4 .


The above declarations are simply helpers that are used in #12 and #13 . If T is a type that works for #3 , then dec has a return type of int , and the following overload is matched:

// #12
template <typename T> typename std::enable_if <sizeof dec <T> (0) != 1, view&>::type operator << (const T&);

because sizeof dec <T> would not be 1.

If T works for #4 (because it didn't work for #3 ), then dec has a return type of int , and the following overload #13 is matched:

// #13
template <typename T> typename std::enable_if <sizeof dec <T> (0) == 1, view&>::type operator << (const T&); 

because sizeof dec <T> would be exactly 1 (the size of a char ).


Note that the real difference is how #12 and #13 are defined . If you see the definitions, they handle the cases where T is streamable or not appropriately.

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