我想写一个通用函数,它接收值为[a1, .. , an]
的container1
,并返回另一个值为[convert(a1), .. , convert(an)]
的container2
。如果container2
是std::vector
,那么问题很琐碎,std::transform
正是我想要的。以下函数可以处理任意的container2
和container1
template<class ToType, class FromType>
ToType convert(const FromType& from)
{
std::vector<typename ToType::value_type> tmp;
std::transform(from.begin(), from.end(),
std::back_inserter(tmp),
[](const typename FromType::value_type& f) {
return convert<typename ToType::value_type>(f);
});
return ToType(tmp.begin(), tmp.end());
}
但它确实是复制的。有人知道如何做得更好吗?
看看这个答案:是否可以编写一个C++模板来检查函数的存在?。您可以使用SFINAE来检测目标容器的函数是否存在(例如push_back
或insert
),或者容器的插入器是否存在(如inserter
或back_inserter
),并相应地执行操作。
另一种方法是创建一个伪迭代器:
template <class T, class U>
struct ConvertIterator {
typedef T dest_type;
typedef U it_type;
ConvertIterator(U&& val) : iterator(std::forward<U>(val)) {
}
bool operator == (const ConvertIterator &other) const {
return iterator == other.iterator;
}
bool operator != (const ConvertIterator &other) const {
return iterator != other.iterator;
}
dest_type operator * () const {
return convert<dest_type>(*iterator);
}
ConvertIterator<T, U> & operator ++() {
++iterator;
return *this;
}
it_type iterator;
};
然后:
template<class ToType, class FromType>
ToType convert(const FromType& from)
{
typedef ConvertIterator<typename ToType::value_type, decltype(from.begin()) > convert_it;
return ToType(convert_it(from.begin()), convert_it(from.end()));
}
这里是一个基于函数的转换迭代器。它具有用于前向迭代器的所有适当的typedef。如果我们选择,我们可以升级它以支持传入Base
迭代器类型的所有标签属性
template<
class Base,
class F,
class R=typename std::result_of<F(decltype(*std::declval<Base const&>()))>::type
>
struct convert_iterator:
std::iterator<std::forward_iterator_tag,typename std::decay<R>::type>
{
Base it;
F f;
template<class It, class Func>
convert_iterator(It&&base, Func&&func):it(std::forward<It>(base)),
// defaulted stuff:
convert_iterator()=default;
convert_iterator(convert_iterator const&)=default;
convert_iterator(convert_iterator &&)=default;
convert_iterator& operator=(convert_iterator const&)=default;
convert_iterator& operator=(convert_iterator &&)=default;
bool operator==(convert_iterator const&other) const {
return it == other.it;
}
bool operator!=(convert_iterator const&other) const { return !(*this==other); }
// a bit overkill, but rvalue and lvalue overrides for these:
R operator*() const& {
return f(*it);
}
R operator*() & {
return f(*it);
}
R operator*() const&& {
return std::move(f)(*std::move(it));
}
R operator*() && {
return std::move(f)(*std::move(it));
}
// normal pre-increment:
convert_iterator& operator++()& {
++it;
return *this;
}
// pre-increment when we are guaranteed not to be used again can be done differently:
convert_iterator operator++()&& {
return {std::next(std::move(it)), std::forward<F>(f)};
}
// block rvalue post-increment like a boss:
convert_iterator operator++(int)& {
return {it++, f};
}
};
创建它们的助手函数:
template< class Base, class F >
convert_iterator<typename std::decay<Base>::type,typename std::decay<F>::type>
make_convert_iterator(Base&& b, F&& f) { return {std::forward<Base>(b), std::forward<F>(f)}; }
接下来,我创建一个处理转换的类。专业化让我们可以针对容器和标量进行不同的调度:
// for scalars:
template<class ToType,class=void>
struct converter {
template<class FromType>
ToType operator()(FromType&& from)const{ return std::forward<FromType>(from); }
};
// attempt at SFINAE test for container:
template<class ToContainer>
struct converter<ToContainer, (void)(
typename std::iterator_traits<
typename std::decay<decltype(std::begin(std::declval<ToContainer&>())>::type
>::value_type
)>
{
using std::begin; using std::end;
using R=std::iterator_traits<typename std::decay<decltype(begin(std::declval<ToContainer&>()))>::type>::value_type;
template<class FromType, class T=decltype(*begin(std::declval<FromType>())>
ToContainer operator()(FromType&& from) const {
auto sub_convert = [](T&& t)->R{
return converter<R>{}(std::forward<T>(t));
};
return {
make_convert_iterator(begin(std::forward<From>(from)), sub_convert),
make_convert_iterator(end(std::forward<From>(from)), sub_convert)
};
};
};
动作转换功能现在是一行:
template<class ToType>
ToType convert(FromType&& from)
{
return converter<ToType>{}(std::forward<FromType>(from));
}