2013-12-18 20 views
6

私は、コンテナ要素の反復のための簡単な一般化関数を書こうとしています。すべての要素はstd::stringに(どのように)変換され、別の場所に格納されます。基本的なバージョンは些細だった:標準ライブラリコンテナの一般化された関数テンプレート

template<class Container> 
void ContainerWork(const Container& c) 
{ 
    for(const auto& elem : c) { 
     /* convert to string and store*/ 
    } 
} 

はそれからに変換値型std::stringとコードとのコンテナの特殊化を追加する必要になった:

template<typename T, template<typename, typename> class Container, class Allocator> 
void ContainerWork(Container<T, Allocator> c) 
{ 
    for(const T& elem : c) { 
     /* convert to string and store*/ 
    } 
} 

template<template<typename, typename> class Container, class Allocator> 
void ContainerWork(Container<std::string, Allocator> c) 
{ 
    for(const std::string& elem : c) { 
     /* frame elem in quotes*/ 
    } 
} 

それは素晴らしい作品が、今私は、シーケンスコンテナを使用することができます(vector,listなど)ですが、setunordered_setも使用します。任意のアイデアはどのように4つのパラメータを持つコンテナの "コピー貼り"の実現なしでこれを行うのですか?私はdecltype(Container)::value_typeと遊ぶことを試みているが運がない。

私は(コンパイラ - VS2012またはGCC 4.8.x)++ 11個の機能Cのほとんどを使用することができ、

答えて

5

あなたはvariadic-templatesを使用することができます

template<typename T, template<typename...> class Container, typename... Args> 
void ContainerWork(Container<T, Args...> c) 
{ 
} 

template<template<typename...> class Container, typename... Args> 
void ContainerWork(Container<std::string, Args...> c) 
{ 
} 

そしておそらくあなたはもちろん、簡単な使用することができるようなものを派遣

template<typename Container> 
void ContainerWork(Container c, 
typename std::enable_if<!std::is_same<typename Container::value_type, 
std::string>::value>::type* = 0) 
{ 
} 

template<typename Container> 
void ContainerWork(Container c, 
typename std::enable_if<std::is_same<typename Container::value_type, 
std::string>::value>::type* = 0) 
{ 
} 

しかし、差は機能のみを変換するための呼び出しであれば、とにかく、 - あなたは単にTためとのためにそれをオーバーロードすることができます、stringのバージョンではこの文字列を単純に返します。

また、あなたは、標準ライブラリのアルゴリズムの全てがイテレータではなく、コンテナ上で動作する理由ですC++ 11 decltype機能

template<typename Container> 
auto ContainerWork(Container c) -> 
decltype(c.begin(), 
typename std::enable_if<!std::is_same<typename Container::value_type, 
std::string>::value, void>::type()) 
{ 
} 

template<typename Container> 
auto ContainerWork(Container c) -> 
decltype(c.begin(), 
typename std::enable_if<std::is_same<typename Container::value_type, 
std::string>::value, void>::type()) 
{ 
} 
+0

計算できません...エラー... VS2012でコンパイルします。 – DarkWanderer

+1

@DarkWandererバリデーションテンプレートにはVS2013以降を使用してください。 – rubenvb

+0

@rubenvb:OPはVS2012を言いましたので、コメント – DarkWanderer

7

SFINAEを使用することができます。

コンテナではなくイテレータで機能するようにコア関数を変更できます。これは、関数テンプレートのために存在していない部分特殊化を、必要になりますので、我々は、デリゲートとクラスのトリック使用します:あなたは本当に、あなたは、このラッパーを作成することができますしたい場合は

template <typename It> 
void DoIteratorWork(It start, It end) 
{ 
    DoIteratorWork_Impl<It, typename std::iterator_traits<It>::value_type>::call(start, end); 
} 

template <typename It, typename ValueType> 
struct DoIteratorWork_Impl 
{ 
    static void call(It start, It end) 
    { 
    for (; start != end; ++start) { 
     // operate on *it 
    } 
    } 
}; 

template <typename It> 
struct DoIteratorWork_Impl<It, std::string> 
{ 
    static void call(It start, It end) 
    { 
    for (; start != end; ++start) { 
     // operate on *it 
    } 
    } 
}; 

を:我々は特性クラスまたは他の技術を利用したアルゴリズムの本体内変換を呼び出したい場合は

template <class Container> 
void DoContainerWork(const Container& c) 
{ 
    using std::begin; using std::end; // enable ADL of begin and end 
    return DoIteratorWork(begin(c), end(c)); 
} 
+0

それは優雅です。 'DoIteratorWork'関数に' inline'修飾子を追加することをお勧めします。 – DarkWanderer

+1

@DarkWandererもしコンパイラが最適化のヒントとして何かに注意を払っていると思うなら(私はほとんどそうしないと思います)、自由に行ってください。しかし、それがテンプレートなので、言語的には影響はありません。 – Angew

+0

Visual Studioコンパイラでは、インライン展開に「__inline」設定しかありません。はい、私は、これが有効になっているときに、 'inline'キーワードをコンパイラが見ていると思います。 http://msdn.microsoft.com/en-us/library/z8y1yy88.aspx – DarkWanderer

2
// implement the bodies as functors to allow for partial 
// specialization for value_type: 
template <class value_type> 
struct DoContainerWorkImpl { 
    template<typename Container> 
    void operator()(const Container& c) const 
    { 
    for (auto&& x : c) { 
     // code 
    } 
    } 
}; 
template <> 
struct DoContainerWorkImpl<std::string> { 
    template<typename Container> 
    void operator()(const Container& c) const 
    { 
    for (std::string const& x : c) { 
     // code 
    } 
    } 
}; 

template <class Container> 
void DoContainerWork(const Container& c) 
{ 
    using std::begin; // enable ADL of begin 
    // extract the iterator for the container c: 
    typedef typename std::decay<decltype(std::begin(c))>::type iterator; 
    // extract the value type of the container: 
    typedef typename std::iterator_traits<iterator>::value_type value_type; 
    DoContainerWorkImpl<value_type>()(c); 
} 

別のアプローチは、「ローカル」アルゴリズム一つのことをする、と決定伴うだろう。ここで

は、そのアプローチで刺しです:今

// convert non-strings: 
template<typename T> 
std::string as_string(T&& t) 
{ 
    // converting code goes here 
} 
// block dangling rvalues: 
std::string as_string(std::string&& s) { return std::move(s); } 
// pass lvalues through: 
std::string const& as_string(std::string const& s) { return s; } 
std::string const& as_string(std::string& s) { return s; } 

、我々はあなたのループ本体に次の操作を行います。

NOOPにコンパイル
for(auto&& elem : c) { 
    std::string const& s = as_string(std::forward<decltype(elem)>(elem)); 
    // code using s as a string 
} 

(または、最悪でもいくつかの動き)コンテナがstd::stringのコンテナの場合はコンバージョンコード、そうでない場合はコンバージョンコードです。

参照に割り当てられた一時的な一時的な拡張は、一時的にstd::stringに生成され、イテレータのlvalueの有効期間は、「イテレータが参照を返す文字列コンテナ」のライフタイムを十分長く保持します。

+0

文字列に引用符を追加したいので、 'std :: string as_string(std :: string)'は単なるコピーではありません。そうでなければ、これは私が一番良いと思う解決策です。要素をループすることは複製しないでください(特に、ループ内でコンバージョンを回避するコードを複製する必要がある場合)。受け取るコンテナには、最初のパラメータをvalue_typeとしてテンプレート化することを想定してはいけません。 – SirGuy

+0

@GuyGreerああ、私はそれを逃した - 私は彼が 'std :: string'の不要なコピーを避けようとしていると思った!この場合、同じ計画が適用されます: 'as_string'や' std :: string s = quote_if_string(elem) 'を実行し、' elem'の型に気づかないでください。 – Yakk

+0

@Yakkはい、「文字列を使った作業」を意味します。引用しています。この場合、as_stringのために "完璧な転送"が必要ですか?どんな意味で「象徴」が「価値」であるのか? – ShaKeSPeaR

1

限り、私はこのことを理解して、あなただけの多型ファンクタでstd::for_eachを使用することができます。

namespace detail { 

    template<typename T> 
    std::string do_conversion(const T& item) { 
     // convert to string 
    } 

    template<> 
    std::string do_conversion<std::string>(const std::string& item) { 
     // frame in quotes 
    } 
} 

struct convert_to_string { 
    template<typename T> 
    void operator()(const T& item) { 
     // delegate to free function in namespace scope, because otherwise we 
     // cannot specialize 
     std::string result = detail::do_conversion(item); 
    } 
}; 

// in calling code 

std::for_each(std::begin(container), std::end(container), convert_to_string{}); 

はあなたのためにこの仕事をしていますか?

関連する問題