推断 std::array 的大小?

20
在下面的代码中:
template<size_t N>
int b(int q, const std::array<int, N>& types)
{
    int r = q;
    for (int t : types)
    {
        r = r + t;
    }
    return r;
}

int main()
{
    b<2>(9, { 2,3 });
}

我如何避免在调用 b 函数时必须指定 N 的值为 2?为什么不能自动推断出此类型?如果不指定,将会出现以下错误: 'b': 没有找到匹配的重载函数 'int b(int,const std::array &)': 无法推断出 'N' 的模板参数。
4个回答

23

C++17 std::array 类型模板参数推导 (CTAD)

从 C++17 开始,这个新语言特性标准库所使用,我们可以省略类型模板参数,让以下代码也能正常工作:

main.cpp

#include <array>

int main() {
    std::array a{1, 2, 3};
}

不是使用 std::array<int, 3> a{1, 2, 3};,而是使用:

测试环境:

g++ -ggdb3 -O0 -std=c++17 -Wall -Wextra -pedantic -o main.out main.cpp

如果我们将-std=c++14设置为例子,编译会失败,错误信息如下:

error: missing template arguments before ‘a’

测试环境为Ubuntu 18.04,GCC 7.5.0。


24
很遗憾我们无法仅省略“size”参数。我在 MSVC 15.9 上尝试了一下,但没有成功。 - Keith Russell
2
有没有什么方法可以减去大小但不改变类型?因为我想要的是uint8_t类型而不是int类型,否则会被减去。显式的uint8_t字面量也很棘手/混乱。 - Anton Krug
1
@AntonKrug 我找不到任何东西,如果你找到了,请告诉我 :-) - Ciro Santilli OurBigBook.com

8

模板参数推导依赖于实参和形参之间的直接类型匹配。实参是一个初始化列表,它不能匹配 array 类型(最好情况下只能匹配 std::array 中内部的原始数组,但是语言规则不支持这样做)。

相反,您可以使用原始数组,如下所示:

#include <stddef.h>
#include <array>

template<size_t N>
int b(int q, int const (&types)[N] )
{
    int r = q;
    for (int t : types)
    {
        r = r + t;
    }
    return r;
}

int main()
{
    b( 9, { 2, 3 } );
}

或者,如果您在编译时不需要绝对的N,您可以使用std::initializer_list

还有许多其他可能相关的方法(例如,可变参数模板函数,或定义一个运算符来构建一个std::vector),但很难说哪种方法适合您未公开的目的。


啊,抱歉,我的目的是传递一个整数数组,该数组将与结构体的整数mType进行检查,对于我正在做的事情来说,原始数组似乎已经足够好了。 - paulm

6
自 C++20 开始,我们可以使用 std::to_array() 进行类型和大小推导。
在您的情况下:
int b(int q, const auto& types)
{
    int r = q;
    for (int t : types)
    {
        r = r + t;
    }
    return r;
}

b(9, std::to_array({2, 3}));
// --- or ---
b(9, std::to_array<int>({2, 3}));

个人而言,我更喜欢在函数参数中使用std::span(或者gsl::span)。

1

要推断std::array的大小,您可以使用通用lambda表达式(C++14):

auto b = [](int q, const auto& types) {
  int r = q;
  for (int t : types)
  {
      r = r + t;
  }
  return r;
};

int main() {
    std::array<int, 2> arr = {{2,3}};
    b(9, arr);
}

当编译器实现 std::make_array(它在库基础 TS 中)时(或者你使用 自己的 实现),在构造时不需要传递数组大小,只需使用即可。
b(9, std::make_array(2,3));

8
读者应该注意,这里的 auto 没有做任何普通 template 不能做到的事情。这段代码之所以能够工作,是因为实际参数的类型被指定为 std::array,而不是因为 auto。使用本答案中的 main 函数,原始问题中的 b 定义也将能够工作。 - Cheers and hth. - Alf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接