用c++ 11的initializer_list实现一个类似std::array的容器
Implementing a std::array-like container with a C++11 initializer_list
std::array
的唯一和我非常不方便的警告是,它不能像内置C数组一样从初始化列表中推断出它的大小,它的大小必须作为模板传递。
是否有可能实现一个std::数组类容器(一个薄包装围绕一个内置的C数组)与C++11
initializer_list?
是因为,与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元素[size],必须有恒定的大小和initializer_list的size()成员函数不是恒定的。
另外,我想知道是否有可能使用可变模板实现这样的容器,尽管从我所读到的,我认为这是不可能的。
如果容器的构造函数接受std::initializer_list,则必须复制值(除非它只是对初始化器的常量引用,这不是很有用)。
是否有可能实现std::数组类容器(围绕内置C数组的薄包装)与c++ 0x initializer_list?
是的,只要你愿意作弊。正如Mooing Duck指出的那样,不,甚至不作弊,除非编译器实现允许。尽管如此,仍然有可能达到足够接近的效果——可以使用初始化列表和由包装器隐藏的静态数组。
这是我为我的个人工具箱写的一些代码。关键是完全忽略大小,即使对于数组也是如此,让提供程序容器处理它;在本例中,initializer_list
可以通过std::distance
提供大小,从而避免了客户端大小说明(似乎是我刚刚发明的术语)。
既然它是"任何人都可以想出它"的那种代码,那么将它"返回"给公众是没有问题的;事实上,我是从Freenode的##c++
频道的某个专家那里得到这个想法的,我不记得他的名字了,所以我猜这个认可是给他们的:
* 编辑 *艾德:
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之前使用它;虽然需要将数据数组预先声明为它自己的变量,但在我看来,这是最接近原始意图的方法(而且它的优点是可用且不会引起eg)。:范围问题)。(也测试了上面的编译器,加上Debian Wheezy的GCC):
using lpp::carray;
short data[]= {1, -7, 4, 188};
carray<short const> CS (data);
!没有"size"参数!
如果没有提供初始化列表,还需要提供一个构造函数来指定大小。
对不起,这是我没有设法实现的一个功能。问题是如何从外部源(可能是Allocator)"静态"地分配内存。假设它可以通过辅助函函数allocate
以某种方式完成,那么构造函数将是这样的:
explicit carray (size_t N)
: ax(allocate(N)), sx(N) {}
我希望这段代码是有帮助的,因为我看到这个问题或多或少是旧的。
这个怎么样?我使用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;
}
我认为这个问题真的很简单。您需要将类型的大小设置为初始化时使用的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
非常简单。它甚至没有操作符[]。
第三个可能是最烦人的。但这也很容易纠正:
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;
};
;问题解决了。初始化列表构造函数确保您可以直接从初始化列表创建一个。虽然不能再省略复制,但它仍然只是复制一对指针。
- 库函数需要一个 std::function<void(void)>,如何传入类函数?
- 将 std::array 移动到另一个 std::array
- 为什么 Clang std::ostream 写一个 std::istream 无法读取的双精度?
- 访问 std:vector 的类成员 std:vector 在一个类中与另一个 std:vector
- 是否有一个 std::set 函数来确定不超过数字 x 的最大元素?
- 除了 std::vector 之外,是否有一个 std 容器不会复制和销毁作为类的元素?
- 我正在将一个 std::string 传递给一个 boost 函数,该函数对该类型进行常量引用,但该值发生了变化
- 在线程 A 中创建一个 std::thread 对象,在线程 B 中连接
- 使用 glDrawElements 绘制一个 std::vector
- 从 C 字符串构造 std::string 与从另一个 std::string 构造 std::string 不一致
- 我可以得到一个字符 * 到一个 std::sregex_iterator 匹配 str() 吗?
- 如何有效地将(一些)项目从一个std::map移动到另一个std::map
- 我可以制作一个std::set的constexpr对象吗
- 打印一个带有静态 int 的函数,有一个 std::cout 和多个 std::cout 有什么区别?
- Visual Studio 2017 STL 可视化工具失败了一个 std::map<MyIntrusivePtr, std::tuple<....> >
- 如何设置一个 std::vector 与另一个,其中两个是不同类的向量?
- SWIG:传递一个 std::vector< std::vector <double> >指向 python 的指针
- 如何构造一个 std::variant 类型对象,其自身 Templated 和构造函数转发参数
- 如何声明一个 std::用不同值内联初始化的结构数组
- 将对象从一个 std::d eque 移动到另一个的更好方法