我正在尝试编写一个类型特征来检测某个类型是否有重载的运算符<<(lt)适合用于输出流。
我错过了一些东西,因为我总是认为一个简单的空类没有操作符。
这里是代码:
template<typename S, typename T>
class is_streamable
{
template<typename SS, typename TT>
static auto test(SS&& s, TT&& t)
-> decltype(std::forward<SS>(s) << std::forward<TT>(t));
struct dummy_t {};
static dummy_t test(...);
using return_type = decltype(test(std::declval<S>(), std::declval<T>()));
public:
static const bool value = !std::is_same<return_type, dummy_t>::value;
};
class C {};
int main() {
std::cout << is_streamable<std::stringstream, C>::value << std::endl;
return 0;
}
输出:
1
这里是ideone:https://ideone.com/ikSBoT
我做错了什么?
答案 0 :(得分:38)
显然operator<<
的这个超载会踩到你的方式并使traling返回类型中的表达式有效:
template< class CharT, class Traits, class T >
basic_ostream< CharT, Traits >& operator<<( basic_ostream<CharT,Traits>&& os,
const T& value );
见this reference page上的(3)。它是一个简单的转发器(调用os << value
),它是在C ++ 11中添加的,允许插入rvalue-streams,因为它们不会绑定到带左值引用的重载。
所以,问题是std::declval<SS>()
返回一个右值引用并且这个重载开始了。调用本身是格式良好的,但是因为函数本身没有实例化,所以你不会得到错误即使价值不可流动。
如果你明确要求左值参考,可以回避这个问题:std::declval<SS&>()
。
我还建议稍微不同的实现,而不将流和值传递给test
。您可以直接在declval
内使用decltype
。与逗号运算符一起,它看起来像这样:
#include <type_traits>
#include <utility>
#include <iostream>
#include <sstream>
template<typename S, 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<S,T>(0))::value;
};
class C {};
int main() {
std::cout << is_streamable<std::stringstream, C>::value << std::endl;
return 0;
}
答案 1 :(得分:4)
我不完全确定问题是什么,但是如果您删除了std::forward
,它就有效,而且我认为无论如何它们都不是必需的:
template<typename SS, typename TT>
static auto test(SS&& s, TT&& t) -> decltype(s << t);
答案 2 :(得分:4)
TheThruth(const bool& t)
)时, jrok's answer会导致链接错误。所以现在在C ++ 17中我们有模板void_t
。基于CPPReference上的示例,我编写并测试了以下内容:
#include <iostream>
#include <typeinfo>
template<typename S, typename T, typename = void>
struct is_to_stream_writable: std::false_type {};
template<typename S, typename T>
struct is_to_stream_writable<S, T,
std::void_t< decltype( std::declval<S&>()<<std::declval<T>() ) > >
: std::true_type {};
class Foo
{
public:
Foo(){}
};
void TheTruth(const bool& t)
{
std::cout<< t<< std::endl;
}
int main() {
std::cout<< is_to_stream_writable<std::ostream,int>::value <<std::endl;
std::cout<< is_to_stream_writable<std::ostream,Foo>::value <<std::endl;
TheTruth( is_to_stream_writable<std::ostream,int>::value );
}
另请注意,is_to_stream_writable
名称更适合operator <<
并为is_from_stream_readable
建议名称:operator >>
(欢迎使用更好的名称建议)。
代码使用g++ -std=c++1z -O0 -Wall -pedantic main.cpp
,gcc版本6.2和7.2以及Coliru进行编译。
答案 3 :(得分:3)
“灵感来自”ypw的答案(ypw - 如果你相应地编辑你的 - 或者创建一个新的来消除掉票 - 我将删除它并提升你的):
template <typename T>
class is_streamable
{
template <typename U> // must be template to get SFINAE fall-through...
static auto test(const U* u) -> decltype(std::cout << *u);
static auto test(...) -> std::false_type;
public:
enum { value = !std::is_same<decltype(test((T*)0)), std::false_type>::value };
};
这个答案的要点是强调对于这个问题,所有对rvalue /左值引用,declvar
,forward
等的担心毫无意义。请记住,我们只是在编译时断言支持流表示法 - 运行时效率考虑因素没有运行时间,例如对事物的引用类型,也不需要使用declvar
来创建流似乎没有可用的流。这段代码保持简单,我相信它具有完全的实用性 - 相反的证据是最受欢迎的。
答案 4 :(得分:1)
编辑:由@jrok发现,它存在一个通用运算符&lt;&lt;对于非常相互影响的右值流。
这里有些问题,如果你看一下coliru上测试的下面的代码,那么最后两行就会编译,即使它们不应该编译......
std::stringstream ss;
B b;
int v;
std::cout << typeid(decltype(ss>>v )).name() << "\n" ;
std::cout << typeid(decltype(ss<<1 )).name() << "\n" ;
std::cout << typeid(decltype(std::declval<std::stringstream>()>>v )).name() << "\n" ;
std::cout << typeid(decltype(std::declval<std::stringstream>()<<1 )).name() << "\n" ;
//std::cout << typeid(decltype(ss>>b )).name() << "\n" ; // do not compile
//std::cout << typeid(decltype(ss<<b )).name() << "\n" ; // do not compile
std::cout << typeid(decltype(std::declval<std::stringstream>()>>b )).name() << "\n" ; // should not compile but succeed
std::cout << typeid(decltype(std::declval<std::stringstream>()<<b )).name() << "\n" ; // should not compile but succeed
答案 5 :(得分:0)
并测试“ >>”运算符:
template<typename I, typename T> class is_istreamable
{
template<typename II, typename TT>
static auto test (int) -> decltype (std::declval<II&>() >> std::declval<TT&>(), std::true_type ());
template<typename, typename>
static auto test (...) -> std::false_type;
public:
static const bool value = decltype (test<I,T> (0))::value;
};