Following the question How can I detect if a type can be streamed to an std::ostream? I've written a trait class that says if some type can be streamed to an IO stream. The trait has seemed to work well until now that I've discovered a problem.
I'm using the code inside a project that uses LLVM and I'm using their StringRef class (which is similar in spirit to the proposed std::string_view). Here is a link to the Doxygen doc for the class, from where you can find it's declaration header file if needed. Since LLVM doesn't provide an operator<< to stream StringRef objects to std streams (they use a custom lightweight stream class), I've written one.
However, when I use the trait it doesn't work if my custom operator<< is declared after the trait (this happens because I have the trait in one header and the operator<< function in another one). I used to think that the lookup in template instantiations worked from the point of view of the instantiation point, so I thought it should work. Actually, as you can see below, with another class and its custom operator<<, declared after the trait, everything works as expected (that's why I've discovered this problem only now), so I can't figure out what makes StringRef special.
This is the complete example:
#include <iostream>
#include "llvm/ADT/StringRef.h"
// Trait class exactly from the cited question's accepted answer
template<typename T>
class is_streamable
{
template<typename SS, typename TT>
static auto test(int)
-> decltype(std::declval<SS&>() << std::declval<TT>(),
std::true_type());
template<typename, typename>
static auto test(...) -> std::false_type;
public:
static const bool value = decltype(test<std::ostream,T>(0))::value;
};
// Custom stream operator for StringRef, declared after the trait
inline std::ostream &operator<<(std::ostream &s, llvm::StringRef const&str) {
return s << str.str();
}
// Another example class
class Foo { };
// Same stream operator declared after the trait
inline std::ostream &operator<<(std::ostream &s, Foo const&) {
return s << "LoL\n";
}
int main()
{
std::cout << std::boolalpha << is_streamable<llvm::StringRef>::value << "\n";
std::cout << std::boolalpha << is_streamable<Foo>::value << "\n";
return 0;
}
Contrary to my expectations, this prints:
false
true
If I move the declaration of the operator<< for StringRef before the trait declaration, it prints true. So why is this strange thing happening and how can I fix this issue?