如何检测类型是否可以流式传输到 std::ostream
How can I detect if a type can be streamed to an std::ostream?
我正在尝试编写一个类型特征来检测类型是否具有适合用于输出流的重载运算符<<()。
我错过了一些东西,因为我总是对一个根本没有运算符的简单空类感到满意。
这里的代码:
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
我做错了什么?
,正是这种operator<<
的重载阻碍了您的方式并使traling返回类型中的表达式有效:
template< class CharT, class Traits, class T >
basic_ostream< CharT, Traits >& operator<<( basic_ostream<CharT,Traits>&& os,
const T& value );
请参阅此参考页上的 (3)。它是在 C++11 中添加的一个简单的转发器(调用 os << value
),以允许插入到重值流,因为它们不会绑定到采用左值引用的重载。
因此,问题是std::declval<SS>()
返回一个右值引用,并且此重载启动。调用本身的格式正确,但由于函数本身未实例化,即使值不可流式传输,也不会收到错误。
如果您明确要求左值引用,则可以回避这一点:std::declval<SS&>()
。
我还建议一个稍微不同的实现,而不将流和值传递给test
。您可以直接在 decltype
中使用declval
。与逗号运算符一起,它看起来像这样:
#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;
}
> 当值传递给需要左值的函数时,jrok 的答案会导致链接错误(即 TheThruth(const bool& t)
)。所以现在在 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 进行编译。
完全确定问题是什么,但是如果您删除std::forward
s,它会起作用,而且我认为无论如何它们在这里都没有必要:
template<typename SS, typename TT>
static auto test(SS&& s, TT&& t) -> decltype(s << t);
现场示例
一种简单的方法...
template <typename T, class = void>
struct is_streamable : std::false_type { };
template <typename T>
struct is_streamable<T, std::void_t<decltype(std::cout << *(T*)0)>>
: std::true_type { };
<小时 />或者,受到 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_v<decltype(test((T*)0)), std::false_type> };
};
讨论
这个答案的要点是强调所有关于右值/左值引用、declvar
、forward
等问题的担忧都是毫无意义的。 请记住,我们只是在做一个支持流式处理符号的编译时断言 - 运行时没有运行时考虑效率,例如对物质的引用类型,也不需要使用 declvar
来创建流,就好像没有可用的一样。 这段代码保持简单,我相信它有充分的实用性 - 相反的证据最受欢迎。
编辑:正如@jrok所发现的,它存在一个泛型运算符<<用于交互不良的右值流。
这里真的出了点问题,如果你看看下面在 coliru 上测试的代码,最后 2 行编译即使它们不应该......
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
并测试">>"运算符:
template<typename IOS, typename T> class can_read_from_ios
{
static_assert (std::is_base_of<std::ios, IOS>::value);
template<typename ios, typename t>
static auto test (int) -> decltype (std::declval<ios&> () >> std::declval<t&> (), std::true_type ());
template<typename, typename> static auto test (...) -> std::false_type;
public:
static const bool value = decltype (test<IOS, T> (0))::value;
};
- 使用std::multimap迭代器创建std::list
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 如何导出包含具有"std::unique_ptr"值的"std::map"属性的
- 从持续时间构造std::chrono::system_clock::time_point
- std::具有相同基类的类的变体
- 如何将字节从 std::ostream 流式传输到 std::vector<uint8_t>?
- 在C 中,是否有可能在不兼容类型的std ::向量对象之间传输不同类型的缓冲区
- 为什么将 std::d ouble_t 流式传输到 std::cout 不会给我小数位?
- 为什么当我将 tolower() 的结果流式传输到 std::cout 时我会得到一个数字,而当我使用 putchar(
- 为什么 std::numeric_limits<float>::min() 在流式传输到具有不同函数的输出时行为不同?
- 为什么 std::setprecision(6) 在固定宽度模式式传输超过 6 位数字
- 在 C++11 中,std::atomic 是否可以用于在两个线程之间传输非原子数据
- 在 std::unique_ptr 类成员上流式传输
- C++ 序列化 std::error_code 以便通过网络传输或保存到磁盘
- 将数据流式传输到 std::vector<double> 崩溃
- std::原子内存屏障可以用来在线程之间传输非原子数据吗?
- 正在传输std::字符串流
- 如何检测类型是否可以流式传输到 std::ostream