【发布时间】:2017-07-13 16:30:46
【问题描述】:
基本上,我想写这样的代码:
std::vector<float> a = { 54, 25, 32.5 };
std::vector<int> b = { 55, 65, 6 };
std::cout << a << b << std::string("lol");
这是不可能的,因为operator<<(ostream&, vector)没有过载
所以,我写了一个函数来完成这项工作:
template<template<typename...> typename T, typename ...Args>
std::enable_if_t<is_iterable_v<T<Args...>>>, std::ostream> &operator<<(std::ostream &out, T<Args...> const &t) {
for (auto const &e : t)
out << e << " ";
out << std::endl;
return out;
}
这很好用,但我的字符串有问题。因为字符串是可迭代的,而且字符串有operator<<函数。
所以我用!is_streamable_out && _is_iterable 之类的另一个特征进行了测试:std::declval<std::ostream&>() << std::declval<T>() 以及它是否具有开始/结束功能。它在 MSVC 上运行良好,但在 Clang 上运行良好(我认为这是因为编译器也使用了我刚刚创建的函数,所以它找到了一个可用于所有方法的重载)。
所以,我目前正在使用!is_same_v<string, T>,但恕我直言,它并不完美。
有没有办法在不重新声明函数的情况下知道函数是否存在?
基本上,我想做这样的事情
if function foo does not exist for this type.
then function foo for this type is ...
这与Is it possible to write a template to check for a function's existence? 的问题不同,因为在另一个线程中,函数并不完全相同(toString 与 toOptionalString)。就我而言,功能是相同的。
这是我的完整代码:
template <class...>
struct make_void { using type = void; };
template <typename... T>
using void_t = typename make_void<T...>::type; // force SFINAE
namespace detail {
template<typename AlwaysVoid, template<typename...> typename Operator, typename ...Args>
struct _is_valid : std::false_type {};
template<template<typename...> typename Operator, typename ...Args>
struct _is_valid<void_t<Operator<Args...>>, Operator, Args...> : std::true_type { using type = Operator<Args...>; };
}
template<template<typename ...> typename Operator, typename ...Args>
using is_valid = detail::_is_valid<void, Operator, Args...>;
#define HAS_MEMBER(name, ...)\
template<typename T>\
using _has_##name = decltype(std::declval<T>().name(__VA_ARGS__));\
\
template<typename T>\
using has_##name = is_valid<_has_push_back, T>;\
\
template<typename T>\
constexpr bool has_##name##_v = has_##name<T>::value
HAS_MEMBER(push_back, std::declval<typename T::value_type>());
HAS_MEMBER(begin);
HAS_MEMBER(end);
template<typename T>
using is_iterable = std::conditional_t<has_begin_v<T> && has_end_v<T>, std::true_type, std::false_type>;
template<typename T>
constexpr bool is_iterable_v = is_iterable<T>::value;
template<class T, class Stream, class = void>
struct can_print : std::false_type {};
template<class T, class Stream>
struct can_print<T, Stream, void_t<decltype(std::declval<Stream&>() << std::declval<const T&>())>> : std::true_type {};
template<class T, class Stream = std::ostream>
constexpr bool can_print_v = can_print<T, Stream>::value;
template<typename T>
std::enable_if_t<is_iterable_v<T> && !can_print_v<T>, std::ostream> &operator<<(std::ostream &out, T const &t) {
for (auto const &e : t)
out << e << " ";
out << std::endl;
return out;
}
template<typename A, typename B>
std::ostream &operator<<(std::ostream &out, std::pair<A, B> const &p) {
out << p.first << " " << p.second << std::endl;
return out;
}
template<typename T>
std::enable_if_t<has_push_back_v<T>, T> &operator<<(T &c, typename T::value_type const &e) {
c.push_back(e);
return c;
}
还有主要的:
#include <iostream>
#include <vector>
#include "Tools/stream.h"
#include <string>
#include <map>
int main() {
std::vector<float> a = { 54, 25, 32.5 };
std::vector<int> b = { 55, 65, 6 };
std::cout << a;
std::cout << has_push_back<float>::value << " " << has_push_back<std::vector<float>>::value << std::endl;
std::cout << is_iterable<std::vector<float>>::value << " " << is_iterable<float>::value << std::endl;
getchar();
return 0;
}
【问题讨论】:
-
std::enable_if_t直到 C++14 才存在,那么你的代码真的是 C++11 吗? -
可以用C++11来实现,所以也可以用C++11来回答这个问题。但是,是的,代码是 C++14(甚至是带有 Clang 的 C++1z)
-
与您的直接问题无关,但您为什么要打扰
...Args?你的函数可以作为一个简单的template<typename T> -
@Antoine Morrier 但
template<typename T>匹配 any 类型,无论是否模板化:pastebin.com/jqYBsvRK