25

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

在下面的代码中wx是等效的(默认初始化int,但不要进入潜在的副本)。有没有办法声明z它具有与 相同的类型y

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

5 回答 5

36

TL;博士

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++中表现得很奇怪。在我们的例子中,具体的问题是数组不允许有任何类型的初始化器。它们被严格限制为使用初始化列表。由初始化列表初始化的临时数组本身不是初始化列表。


答案1:

在这一点上,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++ 对象,而且它们的行为和上面使用的技术晦涩难懂。

答案 2:

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);
于 2013-06-05T21:46:37.880 回答
9

不完全相同,但您可以使用array

auto z = std::array<int, 5>();
于 2013-06-05T20:22:46.220 回答
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 
于 2013-06-05T23:33:16.537 回答
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

于 2018-06-16T00:42:48.237 回答
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;
}
于 2021-06-05T12:31:02.813 回答