Xeo*_*Xeo 133 c++ arrays templates initialization c++11
(注意:这个问题是不必指定元素的数量,仍然允许直接初始化嵌套类型.)
这个问题讨论了C数组的用途int arr[20];
.在他的回答中,@ James Kanze展示了C阵列的最后一个据点,它具有独特的初始化特性:
int arr[] = { 1, 3, 3, 7, 0, 4, 2, 0, 3, 1, 4, 1, 5, 9 };
Run Code Online (Sandbox Code Playgroud)
我们没有必要指定元素的数量,万岁!现在遍历它与C++ 11的功能std::begin
和std::end
从<iterator>
(或您自己的变体),你永远需要甚至认为它的大小.
现在,有没有(可能是TMP)方法实现同样的目标std::array
?使用宏可以使它看起来更好.:)
??? std_array = { "here", "be", "elements" };
Run Code Online (Sandbox Code Playgroud)
编辑:中间版本,从各种答案编译,如下所示:
#include <array>
#include <utility>
template<class T, class... Tail, class Elem = typename std::decay<T>::type>
std::array<Elem,1+sizeof...(Tail)> make_array(T&& head, Tail&&... values)
{
return { std::forward<T>(head), std::forward<Tail>(values)... };
}
// in code
auto std_array = make_array(1,2,3,4,5);
Run Code Online (Sandbox Code Playgroud)
并采用各种酷C++ 11的东西:
sizeof...
std::array
, 当然auto
)这里可以找到一个例子.
但是,正如@Johannes在@Xaade的答案评论中指出的那样,你不能用这样的函数初始化嵌套类型.例:
struct A{ int a; int b; };
// C syntax
A arr[] = { {1,2}, {3,4} };
// using std::array
??? std_array = { {1,2}, {3,4} };
Run Code Online (Sandbox Code Playgroud)
此外,初始化程序的数量仅限于实现支持的函数和模板参数的数量.
Pav*_*aev 61
我能想到的最好的是:
template<class T, class... Tail>
auto make_array(T head, Tail... tail) -> std::array<T, 1 + sizeof...(Tail)>
{
std::array<T, 1 + sizeof...(Tail)> a = { head, tail ... };
return a;
}
auto a = make_array(1, 2, 3);
Run Code Online (Sandbox Code Playgroud)
但是,这需要编译器执行NRVO,然后还跳过返回值的副本(这也是合法的但不是必需的).在实践中,我希望任何C++编译器能够优化它,使其与直接初始化一样快.
Pup*_*ppy 37
我期待一个简单的make_array
.
template<typename ret, typename... T> std::array<ret, sizeof...(T)> make_array(T&&... refs) {
// return std::array<ret, sizeof...(T)>{ { std::forward<T>(refs)... } };
return { std::forward<T>(refs)... };
}
Run Code Online (Sandbox Code Playgroud)
Ker*_* SB 20
结合以前帖子中的一些想法,这里的解决方案甚至适用于嵌套结构(在GCC4.6中测试):
template <typename T, typename ...Args>
std::array<T, sizeof...(Args) + 1> make_array(T && t, Args &&... args)
{
static_assert(all_same<T, Args...>::value, "make_array() requires all arguments to be of the same type."); // edited in
return std::array<T, sizeof...(Args) + 1>{ std::forward<T>(t), std::forward<Args>(args)...};
}
Run Code Online (Sandbox Code Playgroud)
奇怪的是,无法使返回值成为rvalue引用,这对嵌套构造不起作用.无论如何,这是一个测试:
auto q = make_array(make_array(make_array(std::string("Cat1"), std::string("Dog1")), make_array(std::string("Mouse1"), std::string("Rat1"))),
make_array(make_array(std::string("Cat2"), std::string("Dog2")), make_array(std::string("Mouse2"), std::string("Rat2"))),
make_array(make_array(std::string("Cat3"), std::string("Dog3")), make_array(std::string("Mouse3"), std::string("Rat3"))),
make_array(make_array(std::string("Cat4"), std::string("Dog4")), make_array(std::string("Mouse4"), std::string("Rat4")))
);
std::cout << q << std::endl;
// produces: [[[Cat1, Dog1], [Mouse1, Rat1]], [[Cat2, Dog2], [Mouse2, Rat2]], [[Cat3, Dog3], [Mouse3, Rat3]], [[Cat4, Dog4], [Mouse4, Rat4]]]
Run Code Online (Sandbox Code Playgroud)
(对于最后一个输出我正在使用我的漂亮打印机.)
实际上,让我们改善这种结构的类型安全性.我们绝对需要所有类型都一样.一种方法是添加一个静态断言,我在上面编辑过.另一种方法是仅make_array
在类型相同时启用,如下所示:
template <typename T, typename ...Args>
typename std::enable_if<all_same<T, Args...>::value, std::array<T, sizeof...(Args) + 1>>::type
make_array(T && t, Args &&... args)
{
return std::array<T, sizeof...(Args) + 1> { std::forward<T>(t), std::forward<Args>(args)...};
}
Run Code Online (Sandbox Code Playgroud)
无论哪种方式,您都需要可变参数all_same<Args...>
类型特征.这是从归纳std::is_same<S, T>
(注意衰减允许的混合是非常重要的T
,T&
,T const &
等):
template <typename ...Args> struct all_same { static const bool value = false; };
template <typename S, typename T, typename ...Args> struct all_same<S, T, Args...>
{
static const bool value = std::is_same<typename std::decay<S>::type, typename std::decay<T>::type>::value && all_same<T, Args...>::value;
};
template <typename S, typename T> struct all_same<S, T>
{
static const bool value = std::is_same<typename std::decay<S>::type, typename std::decay<T>::type>::value;
};
template <typename T> struct all_same<T> { static const bool value = true; };
Run Code Online (Sandbox Code Playgroud)
请注意,make_array()
copy-of-temporary返回,编译器(具有足够的优化标志!)被允许视为rvalue或以其他方式优化,并且std::array
是聚合类型,因此编译器可以自由选择最佳的构造方法.
最后,请注意在make_array
设置初始化程序时无法避免复制/移动构造.所以std::array<Foo,2> x{Foo(1), Foo(2)};
没有复制/移动,但auto x = make_array(Foo(1), Foo(2));
在转发参数时有两个复制/移动make_array
.我不认为你可以改进,因为你不能将一个可变参数化的初始化列表传递给助手并推断类型和大小 - 如果预处理器有sizeof...
可变参数的函数,也许可以这样做,但不是在核心语言中.
wip*_*ped 11
使用尾随返回语法make_array
可以进一步简化
#include <array>
#include <type_traits>
#include <utility>
template <typename... T>
auto make_array(T&&... t)
-> std::array<std::common_type_t<T...>, sizeof...(t)>
{
return {std::forward<T>(t)...};
}
int main()
{
auto arr = make_array(1, 2, 3, 4, 5);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,对于聚合类,它需要显式类型规范
/*
struct Foo
{
int a, b;
}; */
auto arr = make_array(Foo{1, 2}, Foo{3, 4}, Foo{5, 6});
Run Code Online (Sandbox Code Playgroud)
实际上,这个make_array
实现在sizeof ... operator中列出
感谢类模板提议的模板参数推导,我们可以使用演绎指南来摆脱make_array
帮助
#include <array>
namespace std
{
template <typename... T> array(T... t)
-> array<std::common_type_t<T...>, sizeof...(t)>;
}
int main()
{
std::array a{1, 2, 3, 4};
return 0;
}
Run Code Online (Sandbox Code Playgroud)
用-std=c++1z
x86-64 gcc 7.0下的标志编译
我知道问这个问题已经有一段时间了,但我觉得现有的答案仍有一些缺点,所以我想提出我稍微修改过的版本.以下是我认为缺少一些现有答案的观点.
一些答案提到我们需要依靠RVO来返回构造的array
.那不是真的; 我们可以利用copy-list-initialization来保证永远不会创建临时值.所以代替:
return std::array<Type, …>{values};
Run Code Online (Sandbox Code Playgroud)
我们应该这样做:
return {{values}};
Run Code Online (Sandbox Code Playgroud)
make_array
一个constexpr
功能这允许我们创建编译时常量数组.
首先,如果不是,编译器将发出警告或错误,因为列表初始化不允许缩小.其次,即使我们真的决定做自己的static_assert
事情(也许是为了提供更好的错误信息),我们仍然应该比较参数' 衰变类型而不是原始类型.例如,
volatile int a = 0;
const int& b = 1;
int&& c = 2;
auto arr = make_array<int>(a, b, c); // Will this work?
Run Code Online (Sandbox Code Playgroud)
如果我们只是static_assert
那个a
,b
并且c
具有相同的类型,那么这个检查将失败,但这可能不是我们所期望的.相反,我们应该比较他们的std::decay_t<T>
类型(都是int
s)).
这类似于第3点.使用相同的代码片段,但这次没有明确指定值类型:
volatile int a = 0;
const int& b = 1;
int&& c = 2;
auto arr = make_array(a, b, c); // Will this work?
Run Code Online (Sandbox Code Playgroud)
我们可能想要制作一个array<int, 3>
,但现有答案中的实现可能都没有做到.我们能做的是,而不是返回a std::array<T, …>
,返回a std::array<std::decay_t<T>, …>
.
这种方法有一个缺点:我们不能再返回一个array
cv限定值类型.但大多数时候array<const int, …>
,我们会使用一个类似的东西,而不是像const array<int, …>
.有一个权衡,但我认为是合理的.C++ 17 std::make_optional
也采用这种方法:
template< class T >
constexpr std::optional<std::decay_t<T>> make_optional( T&& value );
Run Code Online (Sandbox Code Playgroud)
考虑到以上几点make_array
,C++ 14中的完整工作实现如下所示:
#include <array>
#include <type_traits>
#include <utility>
template<typename T, typename... Ts>
constexpr std::array<std::decay_t<T>, 1 + sizeof... (Ts)>
make_array(T&& t, Ts&&... ts)
noexcept(noexcept(std::is_nothrow_constructible<
std::array<std::decay_t<T>, 1 + sizeof... (Ts)>, T&&, Ts&&...
>::value))
{
return {{std::forward<T>(t), std::forward<Ts>(ts)...}};
}
template<typename T>
constexpr std::array<std::decay<T>_t, 0> make_array() noexcept
{
return {};
}
Run Code Online (Sandbox Code Playgroud)
用法:
constexpr auto arr = make_array(make_array(1, 2),
make_array(3, 4));
static_assert(arr[1][1] == 4, "!");
Run Code Online (Sandbox Code Playgroud)
(@dyp的解决方案)
注意:需要C++ 14(std::index_sequence
).虽然可以std::index_sequence
在C++ 11中实现.
#include <iostream>
// ---
#include <array>
#include <utility>
template <typename T>
using c_array = T[];
template<typename T, size_t N, size_t... Indices>
constexpr auto make_array(T (&&src)[N], std::index_sequence<Indices...>) {
return std::array<T, N>{{ std::move(src[Indices])... }};
}
template<typename T, size_t N>
constexpr auto make_array(T (&&src)[N]) {
return make_array(std::move(src), std::make_index_sequence<N>{});
}
// ---
struct Point { int x, y; };
std::ostream& operator<< (std::ostream& os, const Point& p) {
return os << "(" << p.x << "," << p.y << ")";
}
int main() {
auto xs = make_array(c_array<Point>{{1,2}, {3,4}, {5,6}, {7,8}});
for (auto&& x : xs) {
std::cout << x << std::endl;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
11505 次 |
最近记录: |