std::array
唯一且非常不方便的警告是,它无法从内置C数组的初始化列表中推断出它的大小,它的大小必须作为模板传递。
是否可以使用C++11
initializer_list实现类似std :: array的容器(内置C数组的薄包装器)?
我问,因为与std :: array不同,它会自动从初始化列表中推断出数组的大小,这样更方便。例如:
// il_array is the hypothetical container
// automatically deduces its size from the initalizer list
il_array <int> myarr = {2, 4, 6, 7, 8};
如果没有提供初始化列表,我们还希望提供一个构造函数来指定大小。例如:
// construct a fixed size array of size 10
il_array <int> myarr2 (10);
这也使容器与其他标准容器更加一致,例如vector,deque和list。
据我所知,它不可能作为包裹的C阵列,例如T elems [size],必须具有常量大小,而initializer_list的size()成员函数不是常量。
另外,我想知道是否有可能使用可变参数模板实现这样的容器,尽管从我读过的内容我认为不可能。
答案 0 :(得分:3)
我觉得你在这里运气不好。 std :: array的最大优点是它是一个POD,可以静态初始化。
如果你有一个带有构造函数的容器采用std :: initializer_list,则必须复制这些值(除非它只是对初始化程序的常量引用,这不是很有用)。
答案 1 :(得分:2)
是否可以使用C ++ 0x initializer_list实现类似std :: array的容器(内置C数组的薄包装器)?
是,好吧,只要你愿意作弊。正如Mooing Duck指出的那样,不,甚至不作弊,unless the compiler implementors let you。虽然,仍然可以足够接近 - 可以使用初始化程序列表和包装程序隐藏的静态数组。
这是我为个人工具箱编写的一些代码。关键是完全忽略大小,即使对于数组,也要让提供者容器处理它;在这种情况下,initializer_list
可以通过std::distance
提供大小,从而避免客户端大小明确(我刚刚发明的一个术语,似乎)。
既然是“任何人都可以提出它”的代码,那么就不会向公众提供“回复”的问题;事实上,我从一个专家那里得到了这个想法,他在Freenode的##c++
频道上不记得了,所以我想这是对他们的认可:
* 修改 * ED:
template <typename T> struct carray {
// typedefs for iterator. The best seems to be to use std::iterator<std::random_iterator_tag,T,int> here
...
template <size_t N>
explicit carray (T (&arr)[N])
: ax(arr), sx(N) {}
// note the linked article.
// This works *only* if the compiler implementor lets you.
carray (std::initializer_list<T> X)
: ax (X.begin()), sx(std::distance(X.begin(),X.end()) {}
// YMMV about the rest of the "rule of N":
// no copy constructor needed -- trivial
// no destructor needed -- data outscopes the wrapper
// no assignment operator needed -- trivial
// container functions, like begin(), end(), size()...
private:
T* ax;
size_t const sx;
};
C ++ 0x模式中的用法和声明非常简单(仅在Fedora 15中使用GCC 4.6进行测试),但它适用于上面外部链接中提到的注意事项,因此它显然是未定义的行为:
using lpp::carray;
carray<short const> CS = {1, -7, 4, 188};
但是,我没有看到为什么编译器实现者不会将积分的initializer_list实现为静态数组。你的电话。
不仅它的工作方式如此,只要您能够在C ++ 0x之前的模式中#ifdef
初始化构造函数,您实际上可以在前C ++ 0x中使用它;虽然将需要将数据数组预先声明为其自己的变量,但它是最接近原始意图的恕我直言(并且它具有可用且不会导致例如:范围问题的优点)。 (也使用上面的编译器,加上Debian Wheezy的GCC测试):
using lpp::carray;
short data[]= {1, -7, 4, 188};
carray<short const> CS (data);
有! 没有“尺寸”参数任何地方!
如果没有提供初始化列表,我们还希望提供一个构造函数来指定大小。
抱歉,这是我没有想要实施的一项功能。问题是如何从外部源(可能是分配器)“静态地”分配内存。假设它可以通过辅助函子allocate
以某种方式完成,那么构造函数将是这样的:
explicit carray (size_t N)
: ax(allocate(N)), sx(N) {}
我希望这段代码有所帮助,因为我看到这个问题或多或少已经过去了。
答案 2 :(得分:1)
这个怎么样?我使用std::tuple
而不是initializer_list
,因为在编译时可以使用元组参数的数量。下面的tuple_array
类继承自std::array
并添加了一个模板化构造函数,该构造函数用于std::tuple
。使用元程序Assign
将元组的内容复制到基础数组存储,该元程序在编译时简单地从N向下迭代到0。最后,make_tuple_array
函数接受任意数量的参数并构造tuple_array
。假设第一个参数的类型是数组的元素类型。好的编译器应该使用RVO消除额外的副本。该程序适用于带有RVO的g ++ 4.4.4和4.6.1。
#include <array>
#include <tuple>
#include <iostream>
template <size_t I, typename Array, typename Tuple>
struct Assign
{
static void execute(Array &a, Tuple const & tuple)
{
a[I] = std::get<I>(tuple);
Assign<I-1, Array, Tuple>::execute(a, tuple);
}
};
template <typename Array, typename Tuple>
struct Assign <0, Array, Tuple>
{
static void execute(Array &a, Tuple const & tuple)
{
a[0] = std::get<0>(tuple);
}
};
template <class T, size_t N>
class tuple_array : public std::array<T, N>
{
typedef std::array<T, N> Super;
public:
template<typename Tuple>
tuple_array(Tuple const & tuple)
: Super()
{
Assign<std::tuple_size<Tuple>::value-1, Super, Tuple>::execute(*this, tuple);
}
};
template <typename... Args>
tuple_array<typename std::tuple_element<0, std::tuple<Args...>>::type, sizeof...(Args)>
make_tuple_array(Args&&... args)
{
typedef typename std::tuple_element<0, std::tuple<Args...>>::type ArgType;
typedef tuple_array<ArgType, sizeof...(Args)> TupleArray;
return TupleArray(std::tuple<Args...>(std::forward<Args>(args)...));
}
int main(void)
{
auto array = make_tuple_array(10, 20, 30, 40, 50);
for(size_t i = 0;i < array.size(); ++i)
{
std::cout << array[i] << " ";
}
std::cout << std::endl;
return 0;
}
答案 3 :(得分:0)
我认为这个问题非常简单。您需要一个大小与initializer_list
一样大小的类型,并使用它来初始化。
// il_array is the hypothetical container
// automatically deduces its size from the initalizer list
il_array <int> myarr = {2, 4, 6, 7, 8};
试试这个:
// il_array is the hypothetical container
// automatically deduces its size from the initalizer list
std::initalizer_list<int> myarr = {2, 4, 6, 7, 8};
这会复制吗?在最技术意义上......是的。但是,专门复制初始化程序列表不会复制其内容。所以这只需要几个指针副本。此外,任何值得使用的C ++编译器都会将此副本遗漏为任何内容。
所以你有它:一个大小已知的数组(通过std::initializer_list::size
)。这里的限制是:
std::initializer_list
非常简陋。它甚至没有operator []。第三个可能是最烦人的。但它也很容易纠正:
template<typename E> class init_array
{
public:
typedef std::initializer_list<E>::value_type value_type;
typedef std::initializer_list<E>::reference reference;
typedef std::initializer_list<E>::const_reference const_reference;
typedef std::initializer_list<E>::size_type size_type;
typedef std::initializer_list<E>::iterator iterator;
typedef std::initializer_list<E>::const_iterator const_iterator;
init_array(const std::initializer_list<E> &init_list) : m_il(init_list) {}
init_array() noexcept {}
size_t size() const noexcept {return m_il.size();}
const E* begin() const noexcept {return m_il.begin();}
const E* end() const noexcept {return m_il.end();}
const E& operator[](size_type n) {return *(m_il.begin() + n);}
private:
std::initializer_list m_il;
};
有;问题解决了。初始化列表构造函数确保您可以直接从初始化列表创建一个。虽然副本不能再被删除,但它仍然只是复制一对指针。