我一直在使用auto
,我注意到在大多数情况下,您可以使用auto
替换变量定义,然后指定类型。
在以下代码中,w
和x
是等效的(默认初始化int
,但不允许进入潜在副本)。有没有办法声明z
,使其与y
具有相同的类型?
int w{};
auto x = int{};
int y[5];
auto z = int[5];
答案 0 :(得分:27)
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 ++的语法,允许您使用this proposal以有序,从左到右的方式命名任何复合类型。
不幸的是,由于C和C ++中的设计错误会引起数组到指针的转换,因此变量z
的推导类型为int*
而不是{{1} }。当临时数组被销毁时,结果变量成为悬空指针。
C ++ 14引入int[5]
,它使用不同的类型推导规则,正确推导出数组类型:
decltype(auto)
但是现在我们遇到了另一个关于数组的设计错误;它们不像正确的物体那样。您不能使用数组分配,复制构造,按值传递等。上面的代码就像是说:
decltype(auto) z = raw_array<int,5>{};
通过所有权利,这应该可行,但遗憾的是C和C ++中的内置数组behave bizarrely。在我们的例子中,具体问题是不允许数组只有任何类型的初始化器;它们严格限于使用初始化列表。由初始化列表初始化的数组临时本身不是初始化列表。
此时,Johannes Schaub提出了一个很好的建议,即我们可以使用临时终身延长。
int g[5] = {};
int h[5] = g;
auto &&z = raw_array<int,5>{};
不是必需的,因为添加decltype(auto)
会更改推断的类型,因此Johannes Schaub的建议适用于C ++ 11。这也避免了对数组初始值设定项的限制,因为我们正在初始化引用而不是数组。
如果您希望数组从初始值设定项中推导出它的长度,则可以使用不完整的数组类型:
&&
虽然上面做了你想要的,你可能更愿意完全避免使用原始数组,因为原始数组的行为不像正确的C ++对象,以及它们的行为模糊和上面使用的技术。
C ++ 11中的template<typename T> using unsized_raw_array = T[];
auto &&z = unsized_raw_array<int>{1, 2, 3};
模板确实像一个正确的对象,包括赋值,可以通过值等等,并且只是内置数组所不具备的一般性和一致性。
std::array
然而,有了这个,你错过了能够让数组类型从初始化器推断自己的长度。相反,您可以编写一个auto z = std::array<int,5>{};
模板函数来进行推理。这是一个非常简单的版本,我没有经过测试,也没有做你想做的事情,例如验证所有参数是相同的类型,还是让你明确指定类型。
make_array
答案 1 :(得分:7)
不完全相同,但您可以使用array
:
auto z = std::array<int, 5>();
答案 2 :(得分:1)
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
答案 3 :(得分:0)
不完全相同的东西,它有点难看,但可以从列表初始化程序中推断出元素类型并直接声明数组,如下所示:
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
。
答案 4 :(得分:0)
最好考虑一下 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;
}