以下代码适用于gcc 4.7。我的想法是拥有这些通用函数,这些函数可用于序列,指针,tupples,pair,用户定义类型等等。如果为某个类型定义了其中一个函数,那么所有函数都应该是。我遇到的问题是确定如何专门化它们。我决定定义一个专门用于每种类型的模板类,实现每个函数,然后定义一个只转发到类内实现的自由函数。
#include <utility>
#include <vector>
#include <iterator>
#include <memory>
#include <iostream>
#include <algorithm>
using namespace std;
template< class M > struct Mon;
template< class X, class F,
class M = Mon< typename decay<X>::type > >
auto mon( X&& x, F f ) -> decltype( M::mon(declval<X>(),f) ) {
return M::mon( forward<X>(x), f );
}
template< class C > struct IsSeqImpl {
// Can only be supported on STL-like sequence types, not pointers.
template< class _C > static true_type f(typename _C::iterator*);
template< class _C > static false_type f(...);
typedef decltype( f<C>(0) ) type;
};
template< class C > struct IsSeq : public IsSeqImpl<C>::type { };
/* Enable if is an STL-like sequence. */
template< class C, class R > struct ESeq : std::enable_if<IsSeq<C>::value,R> { };
template< class Seq >
struct Mon : ESeq< Seq, Seq >::type
{
template< class S, class F >
static S mon( const S& s, F f ) {
S r;
transform( begin(s), end(s), back_inserter(r), f );
return r;
}
};
template< class P > struct IsPtrImpl {
template< class X > static true_type f( X );
template< class X > static false_type f( ... );
typedef decltype( f(*declval<P>()) ) type;
};
template< class P > struct IsPtr : public IsPtrImpl<P>::type { };
template< class P, class R > struct EPtr : enable_if<IsPtr<P>::value,R> { };
template< class X > struct Mon< X* >
{
template< class F, class R = decltype( declval<F>()(declval<X>()) ) >
static unique_ptr<R> mon( X* x, F f ) {
typedef unique_ptr<R> U;
return x ? U( new R(f(*x)) ) : U(nullptr);
}
};
int add_one( int x ) { return x + 1; }
int main()
{
vector<int> v = {1,2,3,4,5};
int x = 5;
auto v2 = mon( v, add_one );
auto x2 = mon( &x, add_one );
// Should print 2 and 6.
cout << v2[0] << '\n';
cout << *x2 << '\n';
}
我想做的是为更多泛型类型专门使用Mon,但是当我尝试再次使用enable_if继承技巧时,gcc抱怨Mon已经定义了。我已经尝试过将第二个模板参数设为SFINAE的true_或false_type的技术,如上所述in this question,但没有运气来编译它。
理想情况下,每当我想到要为其定义动作的类型时,我应该能够编写一个enable_if并在模板专门化中编写整组函数。这节省了为每个函数写入一个enable_if的麻烦。悲观的是,我必须将每个类别中的每个合理类型专门化,以便真正具有通用性。
我可以用通用和可扩展的方式编写吗?
PS:如果只有概念是C ++ 11的一部分。
答案 0 :(得分:3)
警告:恐怕我没有完全理解这个问题,所以我可能会不合时宜......
据我了解,您正在尝试解决编译时调度问题,基本上,您希望的结构是:
foo
你遇到的问题是:
foo_impl
,添加新类型不会影响现有类型foo_impl
。听起来不像简单的功能吗?
这可以通过使用特征相对简单地完成。
void foo_tag_detection(...) {} // void cannot be passed down as an argument
// so if this is picked up we are guaranteed
// a compile-time error
template <typename T>
void foo(T const& t) {
foo_impl(t, foo_tag_detection(t));
}
然后我们创建一个特定于我们类型的标签,这里是序列
// For sequence we'll use a "sequence_tag", it can be reused for other functions
struct sequence_tag {};
实现序列检测,以及我们需要的foo_impl
的重载
template <typename Seq>
auto foo_tag_detection(Seq const& s) -> decltype(s.begin(), s.end(), sequence_tag{}) {
return sequence_tag{};
}
template <typename Seq>
void foo_impl(Seq const& s, sequence_tag) { ... }
请注意,此处使用decltype
触发SFINAE,具体取决于我需要s
支持的操作;它是一个非常强大的机制,并且令人惊讶地简洁。
答案 1 :(得分:2)
通常,该工作的工具是模板专业化,在SFINAE的帮助下。这里有一些未经测试的代码可以让您体验它的样子:
/* Reusable utilities */
template<typename... T>
struct dependent_false_type: std::false_type {};
enum class enabled;
template<typename Cond>
using EnableIf = typename std::enable_if<Cond::value, enabled>::type;
template<typename T>
using Bare = typename std::remove_cv<
typename std::remove_reference<T>::type
>::type;
/* Front-end */
// Second parameter is an implementation detail
template<typename T, typename Sfinae = enabled>
struct Mon {
// It's not allowed to use the primary template
static_assert( dependent_false_type<T>::value
, "No specialization of Mon found" );
// In case a compiler forgets to honour the assertion
template<typename... Ignored>
static void mon(Ignored const&...) = delete;
};
// Front-end that delegates to the back-end
template<
typename T
, typename F
, typename B = Bare<T>
>
auto mon(T&& t, F&& f)
-> decltype( Mon<B>::template mon(std::declval<T>(), std::declval<F>()) )
{ return Mon<B>::template mon(std::forward<T>(t), std::forward<F>(f)); }
/* Back-end for pointers */
template<typename T>
struct Mon<T, EnableIf<std::is_pointer<T>>> {
// Implement somewhere
template<
typename P
, typename F
, typename R = decltype( std::declval<F>()(*std::declval<P>()) )
>
static std::unique_ptr<R> mon(P&& p, F&& f);
};
/* Back-end for ranges */
// Boost.Range does provide range concepts but not range traits
// so let's roll our own crude is_range
namespace detail {
// actual range concepts of Boost.Range also require some member types
// left as an exercise to the reader
template<typename T>
is_range_tester {
template<
typename T
, typename = decltype( boost::begin(std::declval<T>()) )
, typename = decltype( boost::end(std::declval<T>()) )
>
static std::true_type test(int);
template<typename...>
static std::false_type test(long, ...);
};
} // detail
template<typename T>
struct is_range
: decltype( detail::is_range_tester<T&>::template test<T>(0) )
{};
template<typename T>
struct Mon<T, EnableIf<is_range<T>>> {
/* implementation left as an exercise */
};
这也是可扩展的。最大的障碍是每个专业化的条件不得重叠。