2016-04-06 6 views
1

これは奇妙な質問かもしれませんが、サイズd(dはテンプレートパラメータ)の配列を必要とする再帰的テンプレートがあり、d-1テンプレートを同じ要素の場合と同じ配列に渡したいと思います短くこのように、私は、各レベルに対して新しいものを作成するのではなく、1つの配列でしか動作しません。再帰テンプレート - 定数nサイズの配列 - > n-1サイズのインプレース?

私は答えが非常に基本的なものになるかもしれないと感じますが、私が探しているものに近い何かの検索用語を思いつくことはできません。コンテキストにこれを入れて

は、ここでの例では、うまくいけば、私はこれを正しく解釈していますが、あなたはテンプレートとして配列を渡すとき、私はあなたが配列のサイズの引数を渡すと仮定

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

正確には何ですか?その再帰的テンプレートソースを提供できますか? –

+0

例を入れてください。私はただ一つ追加した。 – user2345397

答えて

1

この答えは、静的、Cスタイルの配列です::アレイ、私は謝罪します。

私の頭の上には、再帰を行うための2つの方法がありましたが、もっと多くのテクニックがあります。

最初のものは、部分的に特殊化されたクラス(配列カウントが0)を使用して再帰を終了します。

2番目の方法は、オーバーロードされた関数で再帰を終了する静的に選択された型へのキャストを使用します。ここでは、配列をvoid *にキャストしますが、これでは動作しない型に対しては、元の型から構築可能なカスタム型を作成できます。

私はreinterpret_castを使って配列の型を配列[count]への参照から配列[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'; 
} 
+0

これはすばらしい答えです!私は再解釈のキャストを考えなかったとは信じられません。あなたの答えの残りは、(あなたが示すように)この特定の再帰を実装する際に他にもいくつかの棘があるので、非常に役に立ちました。私はすでに構造体の部分的な特殊化を理解していましたが、投稿した2番目のメソッドについてはわかりませんでした。 – user2345397

+0

ありがとう! 「オーバーロード」バージョンは、type_selectでクラスの特殊化を活用しているため、技術的には専門化にも基づいていますが、本当のオーバーロードに基づく技術はまだ可能であると思います。 reinterpret_castは心配しますが、構造体に埋め込むことによって行われるように、配列全体が何らかの方法でではなく、各要素にアクセスして配列にアクセスする限り、厳密なエイリアシングに違反してはいけません。 –

+0

もう2つのテクニックを追加しました。最後のものがはるかに優れており、本当に再帰を終了する唯一の方法です。 Lol –

0

です。 (そうでなければ、配列の大きさはどのように分かりますか?)配列を渡すと、配列の最初の要素にポインタを渡しているので、次の要素へのポインタを渡すだけですサイズd-1または次の要素とサイズd-1を指すイテレータ。

例:あなたの質問は、STDについてであれば

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

配列のサイズは一定です。サイズはテンプレートパラメータです。 – user2345397

+0

これは私があなたが望むとは思わない場合、テンプレート引数は静的でなければならない。なぜなら関数はコンパイル時と実行時にテンプレート型で構築されているからです。 – Stephen

+0

定数の結果と別の定数の場合、パラメータはまだ一定です。 d-1(dはテンプレートパラメータ)など。 – user2345397

0

。これは次のようになります。

// 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... 
} 
+0

私は実際にはT [d]の入力を新しい配列を作成せずにt [d-1]にする方法について興味がありました。 – user2345397

関連する問題