如何使用auto声明数组

时间:2022-01-06 21:29:46

I have been playing with auto and I noticed that for most cases you can replace a variable definition with auto and then assign the type.

我一直在玩auto,我注意到在大多数情况下,您可以用auto替换变量定义,然后分配类型。

In the following code w and x are equivalent (default initialized int, but lets not get into potential copies). Is there a way to declare z such that it has the same type as y?

在下面的代码中,w和x是等价的(默认初始化int,但不允许进入潜在的副本)。是否有一种方法可以声明z,使其具有与y相同的类型?

int w{};
auto x = int{};
int y[5];
auto z = int[5];

3 个解决方案

#1


24  

TL;DR

template<typename T, int N> using raw_array = T[N];

auto &&z = raw_array<int,5>{};

Your example of auto z = int[5]; isn't legal any more than auto z = int; is, simply because a type is not a valid initializer. You can write: auto z = int{}; because int{} is a valid initializer.

您的auto z = int[5]示例;除了auto z = int不合法;只是因为类型不是有效的初始化器。可以这样写:auto z = int{};因为int{}是一个有效的初始化器。

Once one realizes this, the next attempt would be:

一旦人们意识到这一点,下一次的尝试将是:

auto z = int[5]{};

Note that your int y[5] does not have any initializer. If it had then you would have jumped straight here.

注意,您的int y[5]没有任何初始化器。如果有的话,你就会直接跳到这里。

Unfortunately this does not work either for obscure syntax reasons. Instead you must find a legal way to name the array type in an initializer. For example, a typedef name can be used in an initializer. A handy reusable template type alias eliminates the burdensome requirement of a new typedef for every array type:

不幸的是,由于模糊的语法原因,这也不起作用。相反,您必须找到一种合法的方法来在初始化器中命名数组类型。例如,可以在初始化器中使用typedef名称。一个方便的可重用模板类型别名消除了对每个数组类型的新类型定义的繁重需求:

template<typename T, int N> using raw_array = T[N];

auto z = raw_array<int,5>{};

Aside: You can use template type aliases to fix the weird 'inside-out' syntax of C++, allowing you to name any compound type in an orderly, left-to-right fashion, by using this proposal.

另外,您可以使用模板类型别名来修复c++中怪异的“内隐”语法,允许您使用这个建议,以一种有序的、从左到右的方式来命名任何组合类型。


Unfortunately due to the design bug in C and C++ which causes array-to-pointer conversions at the drop of a hat, the deduced type of the variable z is int* rather int[5]. The resulting variable becomes a dangling pointer when the temporary array is destroyed.

不幸的是,由于在C和c++中的设计错误,导致了在帽子掉下时的arrayto -pointer转换,因此推断变量z的类型是int*而不是int[5]。当临时数组被销毁时,结果变量将成为悬浮指针。

C++14 introduces decltype(auto) which uses different type deduction rules, correctly deducing an array type:

c++ 14介绍了使用不同类型演绎规则的decltype(auto),正确地演绎了一个数组类型:

decltype(auto) z = raw_array<int,5>{};

But now we run into another design bug with arrays; they do not behave as proper objects. You can't assign, copy construct, do pass by value, etc., with arrays. The above code is like saying:

但现在我们又遇到了另一个设计缺陷的数组;它们的行为不像真正的物体。你不能用数组来分配、复制构造、传递值等等。上面的代码是这样说的:

int g[5] = {};
int h[5] = g;

By all rights this should work, but unfortunately built-in arrays behave bizarrely in C and C++. In our case, the specific problem is that arrays are not allowed to have just any kind of initializer; they are strictly limited to using initializer lists. An array temporary, initialized by an initializer list, is not itself an initializer list.

根据所有权限,这应该可以工作,但不幸的是,内置数组在C和c++中表现得很奇怪。在我们的例子中,具体的问题是数组不允许有任何类型的初始化器;它们仅限于使用初始化列表。由初始化器列表初始化的数组临时变量本身不是初始化器列表。


Answer 1:

At this point Johannes Schaub makes the excellent suggestion that we can use temporary lifetime extension.

在这一点上,Johannes Schaub提出了一个很好的建议,我们可以使用临时的寿命延长。

auto &&z = raw_array<int,5>{};

decltype(auto) isn't needed because the addition of && changes the deduced type, so Johannes Schaub's suggestion works in C++11. This also avoids the limitation on array initializers because we're initializing a reference instead of an array.

由于添加&修改了推导的类型,所以不需要使用auto,所以Johannes Schaub的建议在c++ 11中有效。这也避免了对数组初始化器的限制,因为我们正在初始化引用而不是数组。

If you want the array to deduce its length from an initializer, you can use an incomplete array type:

如果您希望数组从初始化器中推断其长度,您可以使用不完整数组类型:

template<typename T> using unsized_raw_array = T[];

auto &&z = unsized_raw_array<int>{1, 2, 3};

Although the above does what you want you may prefer to avoid raw arrays entirely, due to the fact that raw arrays do not behave like proper C++ objects, and the obscurity of their behavior and the techniques used above.

尽管上面的操作符合您的要求,但您可能更希望完全避免原始数组,因为原始数组的行为和上面使用的技术并不像正确的c++对象那样。

Answer 2:

The std::array template in C++11 does act like a proper object, including assignment, being passable by value, etc., and just generally behaving sanely and consistently where built-in arrays do not.

std::在c++ 11中的数组模板表现得像一个合适的对象,包括赋值、可传递值等等,而且一般都表现得很好,而且总是在内置数组不存在的情况下运行。

auto z = std::array<int,5>{};

However, with this you miss out on being able to have the array type infer its own length from an initializer. Instead You can write a make_array template function that does the inference. Here's a really simple version I haven't tested and which doesn't do things you might want, such as verify that all the arguments are the same type, or let you explicitly specify the type.

但是,这样您就错过了让数组类型从初始化器推断其自身长度的机会。相反,您可以编写一个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);

#2


7  

Not quite the same, but you could use array:

不完全相同,但你可以使用数组:

auto z = std::array<int, 5>();

#3


1  

decltype works with g++ 4.9.0 20130601 for this:

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;
}

Output:

输出:

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 

#1


24  

TL;DR

template<typename T, int N> using raw_array = T[N];

auto &&z = raw_array<int,5>{};

Your example of auto z = int[5]; isn't legal any more than auto z = int; is, simply because a type is not a valid initializer. You can write: auto z = int{}; because int{} is a valid initializer.

您的auto z = int[5]示例;除了auto z = int不合法;只是因为类型不是有效的初始化器。可以这样写:auto z = int{};因为int{}是一个有效的初始化器。

Once one realizes this, the next attempt would be:

一旦人们意识到这一点,下一次的尝试将是:

auto z = int[5]{};

Note that your int y[5] does not have any initializer. If it had then you would have jumped straight here.

注意,您的int y[5]没有任何初始化器。如果有的话,你就会直接跳到这里。

Unfortunately this does not work either for obscure syntax reasons. Instead you must find a legal way to name the array type in an initializer. For example, a typedef name can be used in an initializer. A handy reusable template type alias eliminates the burdensome requirement of a new typedef for every array type:

不幸的是,由于模糊的语法原因,这也不起作用。相反,您必须找到一种合法的方法来在初始化器中命名数组类型。例如,可以在初始化器中使用typedef名称。一个方便的可重用模板类型别名消除了对每个数组类型的新类型定义的繁重需求:

template<typename T, int N> using raw_array = T[N];

auto z = raw_array<int,5>{};

Aside: You can use template type aliases to fix the weird 'inside-out' syntax of C++, allowing you to name any compound type in an orderly, left-to-right fashion, by using this proposal.

另外,您可以使用模板类型别名来修复c++中怪异的“内隐”语法,允许您使用这个建议,以一种有序的、从左到右的方式来命名任何组合类型。


Unfortunately due to the design bug in C and C++ which causes array-to-pointer conversions at the drop of a hat, the deduced type of the variable z is int* rather int[5]. The resulting variable becomes a dangling pointer when the temporary array is destroyed.

不幸的是,由于在C和c++中的设计错误,导致了在帽子掉下时的arrayto -pointer转换,因此推断变量z的类型是int*而不是int[5]。当临时数组被销毁时,结果变量将成为悬浮指针。

C++14 introduces decltype(auto) which uses different type deduction rules, correctly deducing an array type:

c++ 14介绍了使用不同类型演绎规则的decltype(auto),正确地演绎了一个数组类型:

decltype(auto) z = raw_array<int,5>{};

But now we run into another design bug with arrays; they do not behave as proper objects. You can't assign, copy construct, do pass by value, etc., with arrays. The above code is like saying:

但现在我们又遇到了另一个设计缺陷的数组;它们的行为不像真正的物体。你不能用数组来分配、复制构造、传递值等等。上面的代码是这样说的:

int g[5] = {};
int h[5] = g;

By all rights this should work, but unfortunately built-in arrays behave bizarrely in C and C++. In our case, the specific problem is that arrays are not allowed to have just any kind of initializer; they are strictly limited to using initializer lists. An array temporary, initialized by an initializer list, is not itself an initializer list.

根据所有权限,这应该可以工作,但不幸的是,内置数组在C和c++中表现得很奇怪。在我们的例子中,具体的问题是数组不允许有任何类型的初始化器;它们仅限于使用初始化列表。由初始化器列表初始化的数组临时变量本身不是初始化器列表。


Answer 1:

At this point Johannes Schaub makes the excellent suggestion that we can use temporary lifetime extension.

在这一点上,Johannes Schaub提出了一个很好的建议,我们可以使用临时的寿命延长。

auto &&z = raw_array<int,5>{};

decltype(auto) isn't needed because the addition of && changes the deduced type, so Johannes Schaub's suggestion works in C++11. This also avoids the limitation on array initializers because we're initializing a reference instead of an array.

由于添加&修改了推导的类型,所以不需要使用auto,所以Johannes Schaub的建议在c++ 11中有效。这也避免了对数组初始化器的限制,因为我们正在初始化引用而不是数组。

If you want the array to deduce its length from an initializer, you can use an incomplete array type:

如果您希望数组从初始化器中推断其长度,您可以使用不完整数组类型:

template<typename T> using unsized_raw_array = T[];

auto &&z = unsized_raw_array<int>{1, 2, 3};

Although the above does what you want you may prefer to avoid raw arrays entirely, due to the fact that raw arrays do not behave like proper C++ objects, and the obscurity of their behavior and the techniques used above.

尽管上面的操作符合您的要求,但您可能更希望完全避免原始数组,因为原始数组的行为和上面使用的技术并不像正确的c++对象那样。

Answer 2:

The std::array template in C++11 does act like a proper object, including assignment, being passable by value, etc., and just generally behaving sanely and consistently where built-in arrays do not.

std::在c++ 11中的数组模板表现得像一个合适的对象,包括赋值、可传递值等等,而且一般都表现得很好,而且总是在内置数组不存在的情况下运行。

auto z = std::array<int,5>{};

However, with this you miss out on being able to have the array type infer its own length from an initializer. Instead You can write a make_array template function that does the inference. Here's a really simple version I haven't tested and which doesn't do things you might want, such as verify that all the arguments are the same type, or let you explicitly specify the type.

但是,这样您就错过了让数组类型从初始化器推断其自身长度的机会。相反,您可以编写一个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);

#2


7  

Not quite the same, but you could use array:

不完全相同,但你可以使用数组:

auto z = std::array<int, 5>();

#3


1  

decltype works with g++ 4.9.0 20130601 for this:

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;
}

Output:

输出:

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