我一直在玩,auto
我注意到在大多数情况下,您可以将变量定义替换为auto
然后分配类型。
在下面的代码中w
和x
是等效的(默认初始化int
,但不要进入潜在的副本)。有没有办法声明z
它具有与 相同的类型y
?
int w{};
auto x = int{};
int y[5];
auto z = int[5];
template<typename T, int N> using raw_array = T[N];
auto &&z = raw_array<int,5>{};
您的示例auto z = int[5];
不合法auto z = int;
,仅仅是因为类型不是有效的初始化程序。你可以写:auto z = int{};
因为int{}
是一个有效的初始化器。
一旦意识到这一点,下一次尝试将是:
auto z = int[5]{};
请注意,您int y[5]
没有任何初始化程序。如果有,那么您会直接跳到这里。
不幸的是,由于晦涩的语法原因,这也不起作用。相反,您必须找到一种合法的方法来在初始化程序中命名数组类型。例如,可以在初始化程序中使用 typedef 名称。一个方便的可重用模板类型别名消除了对每个数组类型的新 typedef 的繁重要求:
template<typename T, int N> using raw_array = T[N];
auto z = raw_array<int,5>{};
旁白:您可以使用模板类型别名来修复 C++ 奇怪的“由内而外”语法,允许您通过使用此提案以有序、从左到右的方式命名任何复合类型。
不幸的是,由于 C 和 C++ 中的设计错误导致了数组到指针的转换,因此推导出的变量类型z
是. 当临时数组被销毁时,结果变量将成为一个悬空指针。int*
int[5]
C++14 介绍decltype(auto)
了使用不同类型推导规则,正确推导数组类型:
decltype(auto) z = raw_array<int,5>{};
但是现在我们遇到了另一个数组设计错误;它们的行为不是正确的对象。您不能使用数组进行分配、复制构造、按值传递等。上面的代码就像是说:
int g[5] = {};
int h[5] = g;
无论如何,这应该可以工作,但不幸的是,内置数组在 C 和 C++中表现得很奇怪。在我们的例子中,具体的问题是数组不允许有任何类型的初始化器。它们被严格限制为使用初始化列表。由初始化列表初始化的临时数组本身不是初始化列表。
在这一点上,Johannes Schaub 提出了一个很好的建议,即我们可以使用临时生命周期延长。
auto &&z = raw_array<int,5>{};
decltype(auto)
不需要,因为添加会&&
更改推导的类型,因此 Johannes Schaub 的建议适用于 C++11。这也避免了对数组初始化器的限制,因为我们正在初始化引用而不是数组。
如果您希望数组从初始化程序中推断出它的长度,您可以使用不完整的数组类型:
template<typename T> using unsized_raw_array = T[];
auto &&z = unsized_raw_array<int>{1, 2, 3};
尽管上面做了你想要的,但你可能更喜欢完全避免使用原始数组,因为原始数组的行为不像正确的 C++ 对象,而且它们的行为和上面使用的技术晦涩难懂。
C++11 中的std::array
模板确实像一个适当的对象,包括赋值、可按值传递等,并且在内置数组不具备的情况下通常表现得理智而一致。
auto z = std::array<int,5>{};
但是,这样一来,您就错过了让数组类型从初始值设定项推断其自身长度的能力。相反,您可以编写一个make_array
执行推理的模板函数。这是一个我没有测试过的非常简单的版本,它没有做你可能想要的事情,比如验证所有参数都是相同的类型,或者让你显式地指定类型。
template<typename... T>
std::array<typename std::common_type<T...>::type, sizeof...(T)>
make_array(T &&...t) {
return {std::forward<T>(t)...};
}
auto z = make_array(1,2,3,4,5);
不完全相同,但您可以使用array
:
auto z = std::array<int, 5>();
decltype
与 g++ 4.9.0 20130601 一起工作:
#include <iostream>
#include <algorithm>
static std::ostream& logger = std::clog;
class A {
static int _counter;
int _id;
public:
A() : _id(++_counter) {
logger << "\tA #" << _id << " c'tored\n";
}
~A() {
//logger << "\tA #" << _id << " d'tor\n";
}
inline int id() const{
return _id;
}
};
int A::_counter(0);
std::ostream& operator<<(std::ostream& os, const A& a) {
return os << a.id();
}
int main() {
auto dump = [](const A& a){ logger << a << " ";};
logger << "x init\n";
A x[5];
logger << "x contains: "; std::for_each(x, x+5, dump);
logger << "\ndecltype(x) y init\n";
decltype(x) y;
logger << "y contains: "; std::for_each(y, y+5, dump);
logger << std::endl;
return 0;
}
输出:
x init
A #1 c'tored
A #2 c'tored
A #3 c'tored
A #4 c'tored
A #5 c'tored
x contains: 1 2 3 4 5
decltype(x) y init
A #6 c'tored
A #7 c'tored
A #8 c'tored
A #9 c'tored
A #10 c'tored
y contains: 6 7 8 9 10
不完全一样的东西,而且有点难看,但是可以从列表初始化器中推导出元素类型,直接声明数组,如下:
template<typename T>
struct array_trait
{
using element_type = T;
array_trait(T(&&)[]);
};
decltype(array_trait({4,5,7}))::element_type a[] = {4,5,7};
std::cout << typeid(a).name() << std::endl;
for (unsigned i = 0; i < 3; i++)
std::cout << a[i] << std::endl;
类型应该是int[3]
,输出应该是4 5 7
。
最好考虑一下 c++14 中的 make_something
#include<iostream>
#include<experimental/array>
using namespace std;
using namespace std::experimental;
int main()
{
auto arr = make_array(1,2,3);
cout << arr.front() << endl;
return 0;
}