Domanda

I thought I had figured out how to make a template which determines if a class/struct is stream-able to an ostream (e.g., "print-able") but there is a flaw in the template I wrote to do this. Looking to learn where I went wrong as well as any solutions to get the template to work the way I expect/want.

So my questions are

  1. Am I missing something pretty basic here or is this more complicated than the template takes into account?
  2. I have not looked at the template's interaction with rvalue references: will they be problematic for the template as well?

Full code here.


Here is the template

template<typename T, typename U>
class isOutputStreamable {
  private:
    template<typename V, typename W> static decltype(operator<<(std::declval<V>(), std::declval<W>()), std::true_type()) check_(int);
    template<typename, typename> static std::false_type check_(...);
  public:
    static const bool value = decltype(check_<T, U>(0))::value;
};

The classes I am testing

struct Empty {
};

struct Streamer {
};

std::ostream& operator<<(std::ostream& ostream, const Streamer&) {
  return ostream << "Streamer";
}

The static_asserts that work the way I expect

// Streamer should show as ostreamable
static_assert( isOutputStreamable< std::ostream&, const Streamer& >::value, "Goodness" );
static_assert( isOutputStreamable< std::ostream&, const Streamer  >::value, "Goodness" );
static_assert( isOutputStreamable< std::ostream&,       Streamer& >::value, "Goodness" );
static_assert( isOutputStreamable< std::ostream&,       Streamer  >::value, "Goodness" );

// isOutputStreamable works with a value ostream for Streamer
static_assert( isOutputStreamable< std::ostream, const Streamer& >::value, "Goodness" );
static_assert( isOutputStreamable< std::ostream, const Streamer  >::value, "Goodness" );
static_assert( isOutputStreamable< std::ostream,       Streamer& >::value, "Goodness" );
static_assert( isOutputStreamable< std::ostream,       Streamer  >::value, "Goodness" );

// Empty should not show as ostreamable
static_assert( !isOutputStreamable< std::ostream&, const Empty& >::value, "Goodness" );
static_assert( !isOutputStreamable< std::ostream&, const Empty  >::value, "Goodness" );
static_assert( !isOutputStreamable< std::ostream&,       Empty& >::value, "Goodness" );
static_assert( !isOutputStreamable< std::ostream&,       Empty  >::value, "Goodness" );

// With a const ostream nothing should show as ostreamable
static_assert( !isOutputStreamable< const std::ostream&, const Streamer& >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream&, const Streamer  >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream&,       Streamer& >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream&,       Streamer  >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream , const Streamer& >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream , const Streamer  >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream ,       Streamer& >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream ,       Streamer  >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream&, const Empty&    >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream&, const Empty     >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream&,       Empty&    >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream&,       Empty     >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream , const Empty&    >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream , const Empty     >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream ,       Empty&    >::value, "Goodness" );
static_assert( !isOutputStreamable< const std::ostream ,       Empty     >::value, "Goodness" );

and then the static_asserts that do not

// Unexpected results: I wanted these to all fail
static_assert( isOutputStreamable< std::ostream, const Empty& >::value, "Badness" );
static_assert( isOutputStreamable< std::ostream, const Empty  >::value, "Badness" );
static_assert( isOutputStreamable< std::ostream,       Empty& >::value, "Badness" );
static_assert( isOutputStreamable< std::ostream,       Empty  >::value, "Badness" );

Thanks in advance for any help.


Update

@Casey: Thanks for the help. I was so convinced that there was something wrong with the template I guess I completely missed the fact that it might actually be matching correctly. As for your suggestion about how to switch the template around this is certainly something that is easily do-able; however, I'm curious if there is not another solution that'll fit given what I'll say next.

The template is actually generated from what I had hoped would be a general macro to create these types of checker templates

   #define BINARY_FUNCTION_EXISTS_(FUNCTION_NAME_, CHECK_NAME_) \
    template<typename T, typename U> class CHECK_NAME_ {\
      private:\
        template<typename V, typename W> static decltype(FUNCTION_NAME_(std::declval<V>(), std::declval<W>()), std::true_type()) check_(int);\
        template<typename, typename> static std::false_type check_(...);\
      public:\
        static const bool value = decltype(check_<T, U>(0))::value;\
    };

Is it the case that this ostream-able template needs to be a special case because of the (undesirable in my case) matching template? I think the answer is yes but I've already been confused once on this and it does not hurt to ask. Thanks again.

È stato utile?

Soluzione

1. The standard library defines an unconstrained template function that provides rvalue stream insertion for all types in terms of lvalue stream insertion:

C++11 §27.7.3.9 Rvalue stream insertion [ostream.rvalue]

template <class charT, class traits, class T>
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>&& os, const T& x);

1 Effects: os << x

2 Returns: os

This is obviously breaking your detection for rvalue ostreams.

2. Since decltype(std::declval<T>()) and decltype(std::declval<T&&>()) are both T&&, it will work just fine with rvalue references; IsOutputStreamable<T, U&&>::value will be equal to IsOutputStreamable<T, U>::value.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top