【问题标题】:Recursive templates - constant n size array -> n-1 size in-place?递归模板 - 常量 n 大小数组 -> n-1 大小就地?
【发布时间】:2016-04-06 23:33:11
【问题描述】:

这可能是一个奇怪的问题,但我有一个递归模板,它需要一个大小为 d 的数组(其中 d 是模板参数),我想将 d-1 模板传递给与它相同的数组元素更短。这样我只使用一个数组,而不是为每个级别创建一个新数组。

我觉得答案可能是非常基本的,但我想不出任何搜索词,结果与我正在寻找的内容相近。

为了说明这一点,这里有一个例子

template<int d>
void Function(int array[d])
{
  array[d- 1]= d;
  Function<d- 1>(?);
}

【问题讨论】:

  • 问题到底是什么?你能提供那个递归模板源吗?
  • 应该举个例子!我刚加了一个。

标签: c++ arrays templates


【解决方案1】:

此答案适用于静态 C 样式数组,如果您的问题是关于 std::Array,我深表歉意。

在我的脑海中,我想出了两种方法来进行递归,但还有更多的技术。

第一个使用部分特化的类(数组计数为零)来终止递归。

第二种方法使用强制转换为静态选择的类型,以重载函数结束递归。在这里,我将数组转换为 void*,但对于不能使用它的类型,您可以创建一个可从原始类型构造的自定义类型。

我求助于使用 reinterpret_cast 将数组的类型从对 array[count] 的引用更改为 array[count-1]。虽然我希望在此处使用它是安全的,但请记住,您可能会在不同的情况下遇到问题。

#include <iostream>

// Ends recursion with a partial specialization
template <typename T, int count>
struct StaticArrayDump {
    static void func(T(&a)[count]) {
        using shorter_t = T(&)[count-1];
        StaticArrayDump<T, count-1>::func(reinterpret_cast<shorter_t>(a));
        std::cout << a[count-1] << ' ';
    }
};
template <typename T>
struct StaticArrayDump<T,0> { 
    static void func(...) {}
};

template <typename T, int count>
static void static_array_dump_spec(T(&a)[count]) {
    using shorter_t = T(&)[count-1];
    StaticArrayDump<T,count>::func(a);
}

// Ends recursion with void* cast and function overload
// Ultimately relies on type_select's specialization, however
template <bool, typename A, typename B> struct type_select /* true */ { using type = A; };
template <typename A, typename B>       struct type_select<false,A,B> { using type = B; };
template <bool cond, typename A, typename B>
using type_select_t = typename type_select<cond, A, B>::type;

static void static_array_dump_ovld(...) {}

template <typename T, int count>
static void static_array_dump_ovld(T(&a)[count]) {
    static const int next_count = count-1;
    using shorter_t = T(&)[next_count];
    static_array_dump_ovld(reinterpret_cast<
            type_select_t<next_count!=0, shorter_t, void*>
        >(a));
    // output the last element
    std::cout << a[count-1] << ' ';
}

// This is an overload-based version which is free of
// any reliance on template specialization.
// helper_trueol's (void*, void*) overload will only be
// selected for arguments (array_ref, count) when count
// is 0, because 0 is the only integer which can be
// converted to a pointer.

// This one's compiler compatibility is a bit shaky...
// MSVC 2013 OK
// IdeOne g++ needs int cast for next_count
static void helper_trueol(void*, void*) {}

template <typename T, int count>
static void helper_trueol(T(&a)[count], int) {
    static const int next_count = count-1;
    using shorter_t = T(&)[next_count];
    helper_trueol(reinterpret_cast<shorter_t>(a), int(next_count));
    std::cout << a[count-1] << ' ';
}

template <typename T, int count>
static void static_array_dump_trueol(T(&a)[count]) {
    helper_trueol(a, count);
}

// Finally, this overload-based version relies
// on SFINAE to disqualify the template function
// as a candidate when count is 0 because the
// zero-length array type triggeres a substitution
// failure.
// So just using this template array argument type,
// the same one used in all of the previous examples,
// but without any extra mechanisms, is all you need
// to end this recursion!  
// This is the obvious best way, of course.

static void static_array_dump_sfinae(...) {}

template <typename T, int count>
static void static_array_dump_sfinae(T(&a)[count]) {
    static const int next_count = count-1;
    using shorter_t = T(&)[next_count];
    static_array_dump_sfinae(reinterpret_cast<shorter_t>(a));
    std::cout << a[count-1] << ' ';
}

//////

int main() {
    double dbl_array[] = { 0, 1.2, 3.4, 5.6789, 10 };
    static_array_dump_spec(dbl_array);
    std::cout << '\n';
    const char* cstr_array[] = { "zero", "one", "two", "three", "four" };
    static_array_dump_ovld(cstr_array);
    std::cout << '\n';

    char charray[] = "Hello";
    charray[sizeof(charray)-1] = '!'; // replace nul terminator
    static_array_dump_trueol(charray);
    std::cout << '\n';

    bool barray[] = {true, true, true, false, true, false, false, false};
    std::cout << std::boolalpha;
    static_array_dump_sfinae(barray);
    std::cout << '\n';
}

【讨论】:

  • 这是一个很好的答案!我不敢相信我没有想到重新解释演员表——我想不确定事情如何处理恒定大小的数组,我不认为只是强制演员表。您的其余答案也非常有帮助,因为(正如您所展示的)在实现此特定递归时还有其他一些棘手的问题。我已经弄清楚了结构的部分专业化,但是我不知道您发布的第二种方法。
  • 谢谢! “重载”版本利用 type_select 中的类特化,因此它在技术上也是基于特化的,但我认为真正的基于重载的技术仍然是可能的。 reinterpret_cast 让我担心,但是只要通过访问每个元素而不是作为一个整体来访问数组,就不应违反严格的别名,就像通过将其嵌入到结构中一样。
  • 我又添加了两种技术。最后一个是迄今为止最好的,也是结束递归的唯一方法。大声笑
【解决方案2】:

希望我正确地解释了这一点,但是当您将数组作为模板传递时,我假设您传递了一个数组大小的参数。 (否则你怎么知道数组有多大?)当你传递数组时,你传递的是一个指向数组第一个元素的指针,所以当你传递子数组时,你可以只传递一个指向下一个元素的指针,然后大小为 d-1 或指向下一个元素且大小为 d-1 的迭代器。

例子:

template< typename T>
T foo(T * ptr, int size) {
    if (size > 0)
        return *ptr + foo(ptr + sizeof(T), size - 1);
    else
        return 0;
}

【讨论】:

  • 数组是固定大小的;大小是一个模板参数。
  • 如果是这种情况,我不认为你想要什么,模板参数必须是静态的,因为函数是在编译时使用模板类型构造的,而不是运行时。
  • 如果参数是一个常数和另一个常数的结果,则该参数仍然是常数。例如 d- 1(其中 d 是模板参数)。
  • 为什么要用模板参数来做这个?将 d 作为函数参数而不是模板参数传递不是更容易吗?
  • 我想看看我是否可以利用我的数组非常小(
【解决方案3】:

根据您提供的信息,我假设您也想知道如何“停止”递归。这看起来像这样:

// this is the function that will be called from the user, it would be bad design too have to pass an integral constant manually when we can easily do this 
template <std::size_t I>
inline 
void Function(int (&_array)[I])
{
  Function(_array, std::integral_constant<std::size_t, I>);
}

// function will recursively do something with an array for each of it's elements
template<std::size_t I>
void Function(int (&_array)[I], std::integral_constant<std::size_t, I>)
{
  // do something...
  Function(_array,std::integral_constant<std::size_t,I-1>);
}

// function as before with a few modifications
template<std::size_t I>
void Function(int (&_array)[I], std::integral_constant<std::size_t, 1>)
{
  // do something...
  // exit function...
}

【讨论】:

  • 其实我只是好奇如何在不创建新数组的情况下获取 T[d] 的输入并使其成为 t[d-1]。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多