0

I'd like use "traits" in order to write a function which prints all values of a list,

template<typename T>
    void print(std::list<T> l) 
    { 
      for (auto item : l)
        cout << item; cout << endl; 
    };

I also tried :

Sorry, my question was not so clear cause i'm just begining with some new c++ features. I tried something like :

template<class T>
enable_if_t<is_convertible_v<T,std::string>> print(std::list<T> l) 
{
    for (auto item : l) std::cout << item << endl; 
};

but not working, visual studio tells there's no operator << for std::tring ...

Thanks for your help.

hamid badi
  • 39
  • 3
  • When you say "traits", do you mean the "concepts" feature in C++20? – Weston McNamara Oct 19 '21 at 18:45
  • 3
    `The type T must be convertible to std::string` Why? – tkausl Oct 19 '21 at 18:47
  • Why must the type be convertible to `std::string`? `std::ostream& operator(std::ostream&, SomeType const&)` can be implemented without `SomeType` being convertible to `std::string`. Furthermore if you pass a type without this operator being available, you'll get a compiler error. The behaviour isn't really much different to anything you could do with the typetraits library or similar features. – fabian Oct 19 '21 at 18:49
  • I guess you want to do a SFINAE on `decltype(cout << l.front())` ? – Ben Voigt Oct 19 '21 at 18:49
  • Your question title asks how to find if it a type *would be* printable with `cout` but the question body implies that `T` is already printable with `cout`. It isn't clear what you need. – François Andrieux Oct 19 '21 at 18:54

1 Answers1

3

If you simply want to cout a std::list<T>, you need no traits. Just write

template<typename T> void print(std::list<T> const& list) {
  for (auto&& t: list) std::cout << t;
}

and the compiler will produce an error if anything doesn't work.

Don't go for the following as long as the above solution is enough

If you want to build compile-time logic based on the "printability" of T (something SFINAE-like), just use a C++20 concept:

template<typename T> concept Ostreamable = requires(T t) { std::cout << t; };

template<Ostreamable T> void print(std::list<T> const& list) {
  for (auto&& t: list) std::cout << t;
}

If you don't have C++20, you can hack your way through in the good old manner:

template<typename T, typename = void> auto constexpr ostreamable_v = false;
template<typename T> auto constexpr ostreamable_v<
  T, std::void_t<decltype(std::cout << std::declval<T>())>
> = true;

template<typename T> std::enable_if_t<ostreamable_v<T>> print(std::list<T> const& list) {
  for (auto&& t: list) std::cout << t;
}

The general pre-C++20 idea is deliberately breaking the more prioritized signatures which don't match your predicate, see SFINAE.

passing_through
  • 1,778
  • 12
  • 24