我的目标是让以下代码正常工作:
#include<iostream>
#include<vector>
#include<list>
#include<algorithm>
#include<array>
#include"functional.hpp"
int main( )
{
std::vector<double> a{1.0, 2.0, 3.0, 4.0};
std::list<char> b;
b.push_back('a');
b.push_back('b');
b.push_back('c');
b.push_back('d');
std::array<int,5> c{5,4,3,2,1};
auto d = zip(a, b, c);
for (auto i : zip(a, b, c) )
{
std::cout << std::get<0>(i) << ", " << std::get<1>(i) << ", " << std::get<2>(i) << std::endl;
}
for (auto i : d)
{
std::cout << std::get<0>(i) << ", " << std::get<1>(i) << ", " << std::get<2>(i) << std::endl;
std::get<0>(i) = 5.0;
//std::cout << i1 << ", " << i2 << ", " << i3 << std::endl;
}
for (auto i : d)
{
std::cout << std::get<0>(i) << ", " << std::get<1>(i) << ", " << std::get<2>(i) << std::endl;
//std::cout << i1 << ", " << i2 << ", " << i3 << std::endl;
}
}具有输出的
1, a, 5
2, b, 4
3, c, 3
4, d, 2
5, a, 5
5, b, 4
5, c, 3
5, d, 2#pragma once
#include<tuple>
#include<iterator>
#include<utility>
/***************************
// helper for tuple_subset and tuple_tail (from http://stackoverflow.com/questions/8569567/get-part-of-stdtuple)
***************************/
template <size_t... n>
struct ct_integers_list {
template <size_t m>
struct push_back
{
typedef ct_integers_list<n..., m> type;
};
};
template <size_t max>
struct ct_iota_1
{
typedef typename ct_iota_1<max-1>::type::template push_back<max>::type type;
};
template <>
struct ct_iota_1<0>
{
typedef ct_integers_list<> type;
};
/***************************
// return a subset of a tuple
***************************/
template <size_t... indices, typename Tuple>
auto tuple_subset(const Tuple& tpl, ct_integers_list<indices...>)
-> decltype(std::make_tuple(std::get<indices>(tpl)...))
{
return std::make_tuple(std::get<indices>(tpl)...);
// this means:
// make_tuple(get<indices[0]>(tpl), get<indices[1]>(tpl), ...)
}
/***************************
// return the tail of a tuple
***************************/
template <typename Head, typename... Tail>
inline std::tuple<Tail...> tuple_tail(const std::tuple<Head, Tail...>& tpl)
{
return tuple_subset(tpl, typename ct_iota_1<sizeof...(Tail)>::type());
// this means:
// tuple_subset<1, 2, 3, ..., sizeof...(Tail)-1>(tpl, ..)
}
/***************************
// increment every element in a tuple (that is referenced)
***************************/
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
increment(std::tuple<Tp...>& t)
{ }
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<(I < sizeof...(Tp)), void>::type
increment(std::tuple<Tp...>& t)
{
std::get<I>(t)++ ;
increment<I + 1, Tp...>(t);
}
/****************************
// check equality of a tuple
****************************/
template<typename T1>
inline bool not_equal_tuples( const std::tuple<T1>& t1, const std::tuple<T1>& t2 )
{
return (std::get<0>(t1) != std::get<0>(t2));
}
template<typename T1, typename... Ts>
inline bool not_equal_tuples( const std::tuple<T1, Ts...>& t1, const std::tuple<T1, Ts...>& t2 )
{
return (std::get<0>(t1) != std::get<0>(t2)) && not_equal_tuples( tuple_tail(t1), tuple_tail(t2) );
}
/****************************
// dereference a subset of elements of a tuple (dereferencing the iterators)
****************************/
template <size_t... indices, typename Tuple>
auto dereference_subset(const Tuple& tpl, ct_integers_list<indices...>)
-> decltype(std::tie(*std::get<indices-1>(tpl)...))
{
return std::tie(*std::get<indices-1>(tpl)...);
}
/****************************
// dereference every element of a tuple (applying operator* to each element, and returning the tuple)
****************************/
template<typename... Ts>
inline auto
dereference_tuple(std::tuple<Ts...>& t1) -> decltype( dereference_subset( std::tuple<Ts...>(), typename ct_iota_1<sizeof...(Ts)>::type()))
{
return dereference_subset( t1, typename ct_iota_1<sizeof...(Ts)>::type());
}
template< typename T1, typename... Ts >
class zipper
{
public:
class iterator : std::iterator<std::forward_iterator_tag, std::tuple<typename T1::value_type, typename Ts::value_type...> >
{
protected:
std::tuple<typename T1::iterator, typename Ts::iterator...> current;
public:
explicit iterator( typename T1::iterator s1, typename Ts::iterator... s2 ) :
current(s1, s2...) {};
iterator( const iterator& rhs ) : current(rhs.current) {};
iterator& operator++() {
increment(current);
return *this;
}
iterator operator++(int) {
auto a = *this;
increment(current);
return a;
}
bool operator!=( const iterator& rhs ) {
return not_equal_tuples(current, rhs.current);
}
typename iterator::value_type operator*() {
return dereference_tuple(current);
}
};
explicit zipper( T1& a, Ts&... b):
begin_( a.begin(), (b.begin())...),
end_( a.end(), (b.end())...) {};
zipper(const zipper<T1, Ts...>& a) :
begin_( a.begin_ ),
end_( a.end_ ) {};
template<typename U1, typename... Us>
zipper<U1, Us...>& operator=( zipper<U1, Us...>& rhs) {
begin_ = rhs.begin_;
end_ = rhs.end_;
return *this;
}
zipper<T1, Ts...>::iterator& begin() {
return begin_;
}
zipper<T1, Ts...>::iterator& end() {
return end_;
}
zipper<T1, Ts...>::iterator begin_;
zipper<T1, Ts...>::iterator end_;
};
//from cppreference.com:
template <class T>
struct special_decay
{
using type = typename std::decay<T>::type;
};
//allows the use of references:
template <class T>
struct special_decay<std::reference_wrapper<T>>
{
using type = T&;
};
template <class T>
using special_decay_t = typename special_decay<T>::type;
//allows template type deduction for zipper:
template <class... Types>
zipper<special_decay_t<Types>...> zip(Types&&... args)
{
return zipper<special_decay_t<Types>...>(std::forward<Types>(args)...);
}我想要几件事:
auto i“值的副本,并且只允许用"auto& i”修改原始容器值,并且应该有一个版本允许您查看,但不允许触摸:"const auto& i"),但我不确定如何解决这个问题。我希望我需要为const模式创建一个const auto& i版本,但我不知道如何为auto i版本创建一个副本。发布于 2013-11-15 10:46:20
我没什么好说的。您的代码读起来相当好,这是相当令人愉快的。不过,以下是一些小道消息:
typedef如果您愿意编写现代代码,则应该考虑删除typedef并在任何地方使用using。它有助于在普通别名和别名模板之间保持一致。此外,=符号有助于直观地分割新名称和它所引用的类型。语法与声明变量的方式也是一致的:
auto i = 1;
using some_type = int;很明显你已经用过了。但是,在其他一些地方,使用它是有意义的:
template <size_t... indices, typename Tuple>
auto tuple_subset(Tuple&& tpl, ct_integers_list<indices...>)
-> decltype(std::make_tuple(std::get<indices>(std::forward<Tuple>(tpl))...))
{
return std::make_tuple(std::get<indices>(std::forward<Tuple>(tpl))...);
// this means:
// make_tuple(get<indices[0]>(tpl), get<indices[1]>(tpl), ...)
}std::enable_if在函数的返回类型中使用std::enable_if时,我发现它往往使其不可读。因此,您可能希望将其移到模板参数列表中。考虑一下您的代码:
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<(I < sizeof...(Tp)), void>::type
increment(std::tuple<Tp...>& t)
{
std::get<I>(t)++ ;
increment<I + 1, Tp...>(t);
}把它和这个比较一下:
template<std::size_t I = 0, typename... Tp,
typename = typename std::enable_if<I < sizeof...(Tp), void>::type>
inline void increment(std::tuple<Tp...>& t)
{
std::get<I>(t)++ ;
increment<I + 1, Tp...>(t);
}根据类型的不同,++var可能比var++更快。它不会为int更改任何内容,但是如果容器包含大类型,请记住,var++中的++通常定义为:
auto operator++(int)
-> T&
{
auto res = var;
++var;
return res;
}如您所见,将创建增量变量的另一个副本,并调用++var。因此,您可能希望在泛型上下文中使用++var而不是var++。
template<typename U1, typename... Us>
zipper<U1, Us...>& operator=(zipper<U1, Us...>& rhs) { ... }您可能希望传递一个const zipper<U1, Us...>&而不是一个zipper<U1, Us...>&。
zipper<T1, Ts...>::iterator& begin() {
return begin_;
}
zipper<T1, Ts...>::iterator& end() {
return end_;
}您还可能希望提供函数begin() const、end() const、cbegin() const和cend() const,如果您希望这组函数对STL是完整的和一致的。另外,一些operator==对于zipper::iterator来说也是很好的。
另外,我喜欢IMHO帮助分隔函数返回类型及其名称的新函数语法,当该返回类型是长的时,它特别有用。然而,我读到有些人不喜欢它,所以这取决于你自己的喜好。
一般来说,您的代码是好的,它的工作,这是更好的。我提供了一些提示,但可能还有许多其他事情可以做,以改善它。当它涉及到可读性时,它总是由您决定的,但是,首选项在该领域很重要:)
编辑:好的,显然是yout的例子很好,但是@Barry的答案似乎突出了更严重的问题。你可能想接受它。
发布于 2013-12-24 01:03:02
对于C++11来说,您的一些元编程机器有点过于复杂。
比较:
template <size_t... n>
struct ct_integers_list {
template <size_t m>
struct push_back
{
typedef ct_integers_list<n..., m> type;
};
};
template <size_t max>
struct ct_iota_1
{
typedef typename ct_iota_1<max-1>::type::template push_back<max>::type type;
};
template <>
struct ct_iota_1<0>
{
typedef ct_integers_list<> type;
};
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
increment(std::tuple<Tp...>& t)
{ }
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<(I < sizeof...(Tp)), void>::type
increment(std::tuple<Tp...>& t)
{
std::get<I>(t)++ ;
increment<I + 1, Tp...>(t);
}相对于:
template<size_t... n> struct ct_integers_list {};
template<size_t... acc> struct ct_iota_1;
template<size_t max, size_t... acc> struct ct_iota_1 : ct_iota_1<max-1, max-1, acc...> {};
template<size_t... acc> struct ct_iota_1<0, acc...> : ct_integers_list<acc...> {};
template<size_t... Indices, typename Tuple>
inline void increment_helper(Tuple& t, ct_integers_list<Indices...>)
{
std::initializer_list<int>{
[&]{ ++std::get<Indices>(t); return 0; }()...
};
}
template<typename... Tp>
inline void increment(std::tuple<Tp...>& t)
{
increment_helper(t, ct_iota_1<sizeof...(Tp)>());
}其想法是让参数包扩展为您完成所有在C++03中您必须通过foo<T>::type、typedefs和std::enable_if等所做的事情。
基本上,您可以用迭代替换大量递归(就像在我的increment_helper中那样);对于必须保持递归的部分,您可以使它看起来更整洁一些,并避免实体的扩散(àla Occam's Razor)。如果您不需要所有这些中间ct_iota_1<...>::type实体,那么就把它们处理掉吧!
但是,如果你想要一个生产质量的ct_integers_list,你应该使用C++14's预定义的std::integer_sequence,或者至少是一个高效的实现,比如这个是Xeo写的。编译器通常将模板递归限制在大约256个级别;您的版本和我的版本都会很快遇到这个限制,而Xeo的则会运行得很好,因为它的递归是O(log max)而不是O(max)。
https://codereview.stackexchange.com/questions/30846
复制相似问题