【问题标题】:Is it possible to construct a fixed sized array in-place as a function argument?是否可以就地构造一个固定大小的数组作为函数参数?
【发布时间】:2019-03-14 21:39:52
【问题描述】:

这主要是关于固定大小数组相关的C++语法的一个角落的问题。

假设我有一个利用类型信息的函数,例如:

template<class T> void fun(T const& t){
    std::cout << typeid(t).name() << std::endl;
}

我可以传递一个值或一个临时对象:

int i;
fun(i); // prints "int" ("i" actually)
fun(int{});   // prints "int" ("i" actually)

但是我不能对数组做同样的事情

double a[10][10];
fun(a); // ok, prints "a[10][10]" ("A10_A10_d" actually)

fun(double[10][10]); // doesn't compile
fun(double{}[10][10]); // doesn't compile
fun(double[10][10]{}); // doesn't compile
fun(double()[10][10]); // doesn't compile
fun(double[10][10]()); // doesn't compile
fun(double(&)[10][10]); // doesn't compile
fun(double(*)[10][10]); // doesn't compile

我原则上可以这样做:

typedef double a1010[10][10];
fun(a1010{});

但是,可以不预定义 typedef 吗?

是否有可能就地构造一个固定大小的数组作为函数参数?

完整代码:

template<class T> void fun(T const& t){
    std::cout << typeid(t).name() << std::endl;
}

typedef double a1010[10][10];

int main(){
    int i;
    fun(i); // prints "int" ("i" actually)
    double a[10][10];
    fun(a); // prints "a[10][10]" ("A10_A10_d" actually)
    fun(a1010{});

    fun(int{});   // prints "int"
/*  fun(double[10][10]); // doesn't compile
    fun(double{}[10][10]); // doesn't compile
    fun(double[10][10]{}); // doesn't compile
    fun(double()[10][10]); // doesn't compile
    fun(double[10][10]()); // doesn't compile
    fun(double(&)[10][10]); // doesn't compile
    fun(double(*)[10][10]); // doesn't compile
    */
    return 0;
}

奖励积分(可能是赏金):可变大小的数组呢?

int N = 10;
f(double[N]);

【问题讨论】:

  • 看看std::array,这可能就是你要找的。此外,你的意思是什么,真的吗?是为了类型检查,还是为了性能?如果您希望提高性能,那么您的尝试几乎肯定是错误的。
  • 你考虑过Variable Template吗?
  • 从好的方面来说,这不是大多数人想要做的事情。
  • @Frax,它纯粹是为了语法糖。通过单个参数传递类型(例如double)和“数字”(10、10)。内容从未使用过,看起来静态数组曾经被分配或保留,因为在 clang 和 gcc 中,我能够在没有堆栈溢出的情况下提取 double a [100000000][1000000000]; gun(a); gun((double[100000000][1000000000]){});。我也希望这些数字也可以是运行时的。
  • 我建议将您的函数更改为template&lt;class T&gt; struct Tag&lt;T&gt; {}; template&lt;class T&gt; void fun(tag&lt;T&gt;){ std::cout &lt;&lt; typeid(T).name() &lt;&lt; std::endl; } 之类的东西,这样您就可以轻松传递类型(非默认可构造类型没有问题,您可以保留右值/左值常量信息,...)。用法类似于fun(tag&lt;double[10][10]&gt;{});fun(tag&lt;decltype(var)&gt;{});

标签: c++ arrays c++11 rvalue temporary-objects


【解决方案1】:

试试:

fun((int[3]){1,2,3});
fun((int[5]){});

至于“奖励点”:可变大小的数组不是语言的一部分。此语言扩展不适用于模板参数:

prog.cc:4:6: 注意:候选模板被忽略:替换失败: 可变修改类型“int [n]”不能用作模板参数 有趣(常量 T&t)

编辑

正如 Chris 所说,上述解决方案建议使用 复合文字,它是 C++ 的扩展。有一个解决方案可以避免这种对 C++ 的扩展,使用一个简单的帮助类:

template <class T, std::size_t N>
struct my_array
{
    T data[N];
};

template <class T, std::size_t N>
void print(const T (&x)[N])
{
     for (auto i: x)
         std::cout << i << '\n';
}

int main()
{
    print(my_array<int,3>{9,10,11}.data);
}

这很好用,但需要将模板参数添加到 my_array,这不是推导的。使用 C++17 可以自动推断类型和大小:

template <class T, std::size_t N>
struct my_array
{
    constexpr my_array(std::initializer_list<T> x)
    {
       std::size_t i = 0;
       for (auto val : x)
           data[i++] = val;
    }
    T data[N];
};
template <class ...T>
my_array(T...) -> my_array<typename std::common_type<T...>::type, sizeof...(T)>;

int main()
{
    print(my_array{9,10,11}.data);
}

对于二维数组,这稍微复杂一些:

template <class T, std::size_t N1, std::size_t N2>
struct my_array2d
{
    constexpr my_array2d(std::initializer_list<std::initializer_list<T> > x)
    {
        std::size_t i = 0;
        for (const auto & row : x) {
            int j=0;
            for (const auto & val: row) {
                data[i][j++] = val;
            }
            i++;
        }
    }
    T data[N1][N2];
};
int main()
{
    work(my_array2d<int, 3, 2>{{9,1},{10,2},{11,3}}.data);
}

我已经放弃了二维数组的推导指南,但我相信它们是可能的。

【讨论】:

  • 完美,这就是我想要的,fun((double[10][10]){})。我以为可变大小的版本行不通。
  • 既然你提到 VLA 不是语言的一部分,那么像 (int[5]){} 这样的复合文字也不是。这些是 C99 功能,就像 VLA 一样。
  • @chris 我添加了一个避免复合文字的解决方案,正如您所指出的,它是 C++ 的扩展
  • 感谢您的编辑,老实说,我想要一种创造性的语法来将类型和几个尺寸一起作为单个参数的一部分传递。 (我对传递内容不感兴趣。)
  • 顺便说一句,原来的解决方案在技术上不是纯C++,-pedantic clang 和 gcc 给warning: ISO C++ forbids compound-literals [-Wpedantic] fun((double[10][10]){}); godbolt.org/z/zgTali
【解决方案2】:

您已经尝试了许多与double 的组合,但您似乎错过了一个。

fun((double[10][10]){});

编译并给出:A10_A10_d

【讨论】:

  • 你没有尝试这个有什么原因吗?
猜你喜欢
  • 1970-01-01
  • 2013-05-17
  • 1970-01-01
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
  • 1970-01-01
  • 2011-10-28
  • 2021-01-07
相关资源
最近更新 更多