Question
Consider the following struct:
template<typename T>
struct stream
{
using type = decltype(
std::declval<std::ostream>() << std::declval<T>()
);
};
template<typename T>
using stream_t = typename stream<T>::type;
The "value" of stream_t<T>
when using certain built-in types (int
, float
, ...) for T
is std::ostream&
, as I expected.
But when using std::string
, char
, int*
, or some streamable dummy struct for T
, the type is an rvalue reference, std::ostream&&
.
Once std::declval<std::ostream>()
(returns an std::ostream&&
) is replaced withstd::declval<std::ostream&>
(returns an std::ostream&
, due to reference collapsing rule, right?) the returned type is the expected std::ostream&
. Is there some rvalue overload of operator<<
that I don't know about?
Why is this happening?
Compiler specs
The results above are obtained with AppleClang 11.0.0.11000033. When using gcc-7.4 instead, the result is always std::ostream&
, as expected.
Complete source
#include <iostream>
#include <type_traits>
/* ************************************
* Sans reference
* ************************************ */
template<typename T>
struct stream
{
using type = decltype(
std::declval<std::ostream>() << std::declval<T>()
);
};
template<typename T>
using stream_t = typename stream<T>::type;
/* ************************************
* With reference
* ************************************ */
template<typename T>
struct stream_ref
{
using type = decltype(
std::declval<std::ostream&>() << std::declval<T>()
);
};
template<typename T>
using stream_ref_t = typename stream_ref<T>::type;
/* ************************************
* Dummy struct
* ************************************ */
struct Dummy
{
friend std::ostream& operator<<(std::ostream&, const Dummy&);
};
/* ************************************
* Static asserts
* ************************************ */
static_assert( std::is_same_v<stream_t<int>, std::ostream&> );
static_assert( std::is_same_v<stream_t<float>, std::ostream&> );
static_assert( std::is_same_v<stream_t<std::string>, std::ostream&&> );
static_assert( std::is_same_v<stream_t<const char*>, std::ostream&&> );
static_assert( std::is_same_v<stream_t<int*>, std::ostream&&> );
static_assert( std::is_same_v<stream_t<Dummy>, std::ostream&&> );
static_assert( std::is_same_v<stream_ref_t<std::string>, std::ostream&> );
static_assert( std::is_same_v<stream_ref_t<const char*>, std::ostream&> );
static_assert( std::is_same_v<stream_ref_t<int*>, std::ostream&> );
static_assert( std::is_same_v<stream_ref_t<Dummy>, std::ostream&> );
int main(int argc, char** argv)
{
return 0;
}
T = int*
is supposed to yieldstd::ostream&&
, your code asserts equality tostd::ostream&
. Nevertheless, the static assert with ` std::ostream&&` fails as well. – Woltersstd::ostream&
, i.e. the middle block ofstatic_asserts
fail, all others pass. – Woltersstd::invoke_result_t(operator<< <std::ostream, T>, declval<std::ostream>(), declval<T>())
? (I am on mobile otherwise I would do it myself) – Spikes