是否可以检查类型或 class 的重载运算符 << 是否存在?
is it possible to check if overloaded operator<< for type or class exists?
我正在尝试使用 C++17 检查编译时是否存在重载运算符 <<。理想情况下,它应该类似于以下内容:
template<typename T>
static void serialize_storage(const void* this_, std::ostream& stream) {
if constexpr (std::is_invocable<...>::value) {
stream << (*reinterpret_cast<const T*>(this_));
} else {
throw std::runtime_error("Type can not be serialized");
}
}
为 is_invocable 形成参数似乎很棘手,因为 operator<< 作为 std::ostream 的成员或仅作为“独立”运算符被重载。所以我先尝试了两个不同的功能。
一个例子:
#include <map>
#include <string>
#include <iostream>
using Mmap = std::map<std::string, std::string>;
std::ostream& operator<<(std::ostream& stream, const Mmap& map) {
stream << map.size();
return stream;
}
template<typename T>
typename std::enable_if<std::is_invocable<decltype (operator <<(std::declval<std::ostream&>(), std::declval<const T&>())), std::ostream&, const T&>::value, void>::type
serialize_storage(const void* this_, std::ostream& stream) {
stream << (*reinterpret_cast<const T*>(this_));
}
template<typename T>
typename std::enable_if<std::is_invocable<decltype (std::declval<std::ostream>().operator <<(std::declval<T>())), std::ostream, T>::value, void>::type
serialize_storage(const void* this_, std::ostream& stream) {
stream << (*reinterpret_cast<const T*>(this_));
}
int main(int , char* [])
{
Mmap foo;
char boo = 'A';
serialize_storage<Mmap>(&foo, std::cerr);
serialize_storage<char>(&boo, std::cerr);
std::cerr << std::endl;
return 0;
}
但是编译器不能替代这两种类型。它看到了候选人,但 std::ostream::operator<<(char) 和重载的 std::ostream& operator<<(std::ostream& stream, const Mmap& map) 都不符合 is_invocable 条件。
您可以像这样添加特征 (is_streamable
):
#include <type_traits>
// A helper trait to check if the type supports streaming
template<class T>
class is_streamable {
// match if streaming is supported
template<class TT>
static auto test(int) ->
decltype( std::declval<std::ostream&>() << std::declval<TT>(), std::true_type() );
// match if streaming is not supported:
template<class>
static auto test(...) -> std::false_type;
public:
// check return value from the matching "test" overload:
static constexpr bool value = decltype(test<T>(0))::value;
};
template<class T>
inline constexpr bool is_streamable_v = is_streamable<T>::value;
然后您可以像这样使用它:
template<class T>
static void serialize_storage(const void* this_, std::ostream& stream) {
if constexpr (is_streamable_v<T>) {
stream << (*reinterpret_cast<const T*>(this_));
} else {
throw std::runtime_error("Type can not be serialized");
}
}
我正在尝试使用 C++17 检查编译时是否存在重载运算符 <<。理想情况下,它应该类似于以下内容:
template<typename T>
static void serialize_storage(const void* this_, std::ostream& stream) {
if constexpr (std::is_invocable<...>::value) {
stream << (*reinterpret_cast<const T*>(this_));
} else {
throw std::runtime_error("Type can not be serialized");
}
}
为 is_invocable 形成参数似乎很棘手,因为 operator<< 作为 std::ostream 的成员或仅作为“独立”运算符被重载。所以我先尝试了两个不同的功能。
一个例子:
#include <map>
#include <string>
#include <iostream>
using Mmap = std::map<std::string, std::string>;
std::ostream& operator<<(std::ostream& stream, const Mmap& map) {
stream << map.size();
return stream;
}
template<typename T>
typename std::enable_if<std::is_invocable<decltype (operator <<(std::declval<std::ostream&>(), std::declval<const T&>())), std::ostream&, const T&>::value, void>::type
serialize_storage(const void* this_, std::ostream& stream) {
stream << (*reinterpret_cast<const T*>(this_));
}
template<typename T>
typename std::enable_if<std::is_invocable<decltype (std::declval<std::ostream>().operator <<(std::declval<T>())), std::ostream, T>::value, void>::type
serialize_storage(const void* this_, std::ostream& stream) {
stream << (*reinterpret_cast<const T*>(this_));
}
int main(int , char* [])
{
Mmap foo;
char boo = 'A';
serialize_storage<Mmap>(&foo, std::cerr);
serialize_storage<char>(&boo, std::cerr);
std::cerr << std::endl;
return 0;
}
但是编译器不能替代这两种类型。它看到了候选人,但 std::ostream::operator<<(char) 和重载的 std::ostream& operator<<(std::ostream& stream, const Mmap& map) 都不符合 is_invocable 条件。
您可以像这样添加特征 (is_streamable
):
#include <type_traits>
// A helper trait to check if the type supports streaming
template<class T>
class is_streamable {
// match if streaming is supported
template<class TT>
static auto test(int) ->
decltype( std::declval<std::ostream&>() << std::declval<TT>(), std::true_type() );
// match if streaming is not supported:
template<class>
static auto test(...) -> std::false_type;
public:
// check return value from the matching "test" overload:
static constexpr bool value = decltype(test<T>(0))::value;
};
template<class T>
inline constexpr bool is_streamable_v = is_streamable<T>::value;
然后您可以像这样使用它:
template<class T>
static void serialize_storage(const void* this_, std::ostream& stream) {
if constexpr (is_streamable_v<T>) {
stream << (*reinterpret_cast<const T*>(this_));
} else {
throw std::runtime_error("Type can not be serialized");
}
}