2017-08-28 15 views
1

ネストされたテンプレートの特殊化が行われているどのように私はテンプレート機能は、次のように定義しているC++

template<typename TNum, int cnt> class Vec 

:私は何をする必要があるか

template<typename TObject> TObject Deserialize(long version, const Value &value) 

は、次のように定義されるベクトルを取る専門を書くことですcntTNumにはまだアクセスしています。私はunsuccesfullyエラーが生じ

template<typename TNum, int cnt> Vec<TNum, cnt> Deserialize<Vec<TNum, cnt>>(long version, Value &value) 

を試してみました

:明示的なテンプレート引数の不正使用は

それを行うための正しい方法は何ですか?

+3

関数を部分的に特殊化することはできません。したがって、実際の実装をダミーのテンプレート構造体の静的メソッドに隠し、その構造体全体を特殊化する必要があります。 – HolyBlackCat

+0

ベクトルまたはコンテナのみ? – rustyx

+0

私の場合、Vecは外部ライブラリからの数学的ベクトルです。 –

答えて

4

通常、関数テンプレートを処理し、それらを部分的に特殊化する必要がある場合の正解は、単純に関数テンプレートをオーバーロードすることです。この場合、テンプレートパラメータに依存する引数がないため、テンプレートパラメータが明示的に指定され、推測されないため、このトリックは直接的には機能しません。ただし、実装関数に進むことができ、単純なタグ構造体を使用してオーバーロード作業を行うことができます。

#include <functional> 
#include <iostream> 
#include <type_traits> 
#include <vector> 
#include <array> 

template <class T> 
struct tag{}; 

template<typename TObject> 
TObject Deserialize_impl(long version, tag<TObject>) { 
    std::cerr << "generic\n"; 
    return {}; 
} 

template<typename T, std::size_t N> 
std::array<T,N> Deserialize_impl(long version, tag<std::array<T,N>>) { 
    std::cerr << "special\n"; 
    return {}; 
} 

template<typename TObject> 
TObject Deserialize(long version) { 
    return Deserialize_impl(version, tag<TObject>{}); 
} 


int main() { 
    Deserialize<int>(0); 
    Deserialize<std::array<int,3>>(0); 

    return 0; 
} 

ライブ例:http://coliru.stacked-crooked.com/a/9c4fa84d2686997a

私は一般的に、あなたが機能を利用することができ、多くのものがあるような静的メソッド(ここでは他の主要なアプローチ)を持つ構造体の部分的な特殊化に強く望ましいこれらのアプローチを見つけますそれは専門化と比較してより直感的に動作します。 YMMV。

+1

私はインプラントに同じ名前を付けることさえあります:異なるarg countsはそれをかなり安全にし、ADLエクステンションをあまり醜くします。 – Yakk

+0

@Yakk公正なポイント;私は、オーバーロードに精通していない人たちのために答えを残しておきたいと思います(再帰のために誤っていないので)。しかし、私はあなたに同意します。 –

0

機能タグディスパッチは素晴らしいアプローチですが、ここでは比較のためのクラス特殊化バージョンがあります。両方ともその使用があり、どちらも本質的に残念な決定だとは思わないが、あなたの個人的なスタイルに一層合っているかもしれない。 カスタムデシリアライズハンドラを必要とするあなたが書い任意のクラス、ちょうどデシリアライザクラスの専門を書く:このような状況では理想的

#include <iostream> 
#include <string> 
using namespace std; 

using Value = std::string; 

// default deserialize function 
template <typename TObject> 
struct Deserializer { 
    static TObject deserialize(long version, const Value &value) { 
     std::cout << "default impl\n"; 
     return TObject(); 
    } 
}; 

// free standing function (if you want it) to forward into the classes 
template <typename TObject> 
TObject deserialize(long version, const Value &value) { 
    return Deserializer<TObject>::deserialize(version, value); 
} 

// Stub example for your Vec class 
template<typename TNum, int cnt> class Vec { }; 

// Stub example for your Vec deserializer specialization 
template <typename TNum, int cnt> struct Deserializer<Vec<TNum, cnt>> { 
    static auto deserialize(long version, const Value &value) { 
     std::cout << "specialization impl: cnt=" << cnt << "\n"; 
     return Vec<TNum, cnt>(); 
    } 
}; 

int main() { 
    Value value{"abcdefg"}; 
    long version = 1; 

    deserialize<int>(version, value); 
    deserialize<Vec<int, 10>>(version, value); 
} 
+0

このアプローチは注目すべき欠点を持っていますが、問題の型だけを返し、受け入れない場合はそれほど明白ではありません。それらの1つは、多型型の方がはるかにうまく機能しないということです。専門分野は継承とうまく調和しません。関数はまた、constへの変換を自由にします。 'T'と' const T'は異なるタイプなので、特殊化すると時々噛まれることもあります! –

+0

@NirFriedmanこれは良い点です。ありがとうございます。 :)しかし、タグのインスタンス化には多態性の関係がないので、タグとタグを受け取るタグ付きディスパッチがどのように異なって動作するかは分かりません。 –

+0

私が言ったように、あなたはここでそれを見ないでしょう。なぜなら、あなたは多形的ではない異なるタイプを返すからです。しかし、あなたが参照によって型を受け入れていたなら、あなたは自由(const資格と同様に)のためにその変換を得るでしょう。主なことは、オーバーロードによる専門化の重複は簡単ではないということです。私の答えはそれを示しています。しかし、オーバーロードを行うと、専門分野を再現するためにはるかに多くの便利なテクニックにアクセスできます。 –

0

VecconstexprあるべきメンバーVec::value_typeVec::size()など、独自のテンプレートパラメータを反映しなければなりません。

クラスが独自のインターフェイスで独自のプロパティを提供できない場合は、次の最良の方法は独自の拡張インターフェイスを定義することです。この状況では、別のメタ関数(アクセサ関数など)または特性クラス(ヘルパービュークラスなど)を使用できます。私は後者を好むでしょう:

template<typename> 
struct vector_traits; 

template< typename TNum, int cnt > 
struct vector_traits< Vec< TNum, cnt > > { 
    typedef TNum value_type; 
    constexpr static int size = cnt; 
}; 

template<typename TVec> TVec Deserialize(long version, Value &value) { 
    typedef vector_traits<TVec> traits; 
    typedef typename traits::value_type TNum; 
    constexpr static int cnt = traits::size; 
    … 
} 

この解決策は、既存の機能に適合し、さらに署名をきれいにします。また、新しいオーバーロード全体ではなく、traitsの特殊化を追加することで、この機能を柔軟に適用できます。

+0

このアプローチでは、Deserializeはベクトル型のみを逆シリアル化することを前提としています。デシリアライズする必要がある無関係なFooクラスがある場合はどうなりますか? vector_traitsをコンパイルするように特化しますか? –

+0

明らかに、関数本体は、概念的にベクトル型でしか動作しません。他の種類(SFINAEまたは概念別)の関数署名を無効にし、より多くのオーバーロードを提供します。 – Potatoswatter

関連する問題