c++ - 関数 - クラステンプレートを使用するにはテンプレート引数リストが必要です




c++テンプレートクラス。 任意のコンテナ型の関数、それを定義する方法は? (2)

ここでは、 この回答の最新かつ拡張されたバージョンと、Sabastianによる回答に対する大幅な改善があります。

この考え方は、STLコンテナのすべての特性を定義することです。 残念ながら、これは非常に高速になり、幸いにも多くの人々がこのコードのチューニングに取り組んできました。 これらの特性は再利用可能ですので、type_utils.hppという名前のファイルのコードの下にコピーしてコピーしてください(これらの名前を自由に変更できます):

//put this in type_utils.hpp 
#ifndef commn_utils_type_utils_hpp
#define commn_utils_type_utils_hpp

#include <type_traits>
#include <valarray>

namespace common_utils { namespace type_utils {
    //from: https://raw.githubusercontent.com/louisdx/cxx-prettyprint/master/prettyprint.hpp
    //also see https://gist.github.com/louisdx/1076849
    namespace detail
    {
        // SFINAE type trait to detect whether T::const_iterator exists.

        struct sfinae_base
        {
            using yes = char;
            using no  = yes[2];
        };

        template <typename T>
        struct has_const_iterator : private sfinae_base
        {
        private:
            template <typename C> static yes & test(typename C::const_iterator*);
            template <typename C> static no  & test(...);
        public:
            static const bool value = sizeof(test<T>(nullptr)) == sizeof(yes);
            using type =  T;

            void dummy(); //for GCC to supress -Wctor-dtor-privacy
        };

        template <typename T>
        struct has_begin_end : private sfinae_base
        {
        private:
            template <typename C>
            static yes & f(typename std::enable_if<
                std::is_same<decltype(static_cast<typename C::const_iterator(C::*)() const>(&C::begin)),
                             typename C::const_iterator(C::*)() const>::value>::type *);

            template <typename C> static no & f(...);

            template <typename C>
            static yes & g(typename std::enable_if<
                std::is_same<decltype(static_cast<typename C::const_iterator(C::*)() const>(&C::end)),
                             typename C::const_iterator(C::*)() const>::value, void>::type*);

            template <typename C> static no & g(...);

        public:
            static bool const beg_value = sizeof(f<T>(nullptr)) == sizeof(yes);
            static bool const end_value = sizeof(g<T>(nullptr)) == sizeof(yes);

            void dummy(); //for GCC to supress -Wctor-dtor-privacy
        };

    }  // namespace detail

    // Basic is_container template; specialize to derive from std::true_type for all desired container types

    template <typename T>
    struct is_container : public std::integral_constant<bool,
                                                        detail::has_const_iterator<T>::value &&
                                                        detail::has_begin_end<T>::beg_value  &&
                                                        detail::has_begin_end<T>::end_value> { };

    template <typename T, std::size_t N>
    struct is_container<T[N]> : std::true_type { };

    template <std::size_t N>
    struct is_container<char[N]> : std::false_type { };

    template <typename T>
    struct is_container<std::valarray<T>> : std::true_type { };

    template <typename T1, typename T2>
    struct is_container<std::pair<T1, T2>> : std::true_type { };

    template <typename ...Args>
    struct is_container<std::tuple<Args...>> : std::true_type { };

}}  //namespace
#endif

これらの特性を使用して、コードがコンテナタイプのみを受け入れることができるようになりました。 たとえば、次のように1つのベクトルを別のベクトルに追加するappend関数を実装できます。

#include "type_utils.hpp"

template<typename Container>
static typename std::enable_if<type_utils::is_container<Container>::value, void>::type
append(Container& to, const Container& from)
{
    using std::begin;
    using std::end;
    to.insert(end(to), begin(from), end(from));
}

私はイテレータの動作を確実にするためにstdの名前空間からbegin()とend()を使用しています。 詳細は私のブログ記事を参照しください。

さて、簡単なテンプレートの質問。 テンプレートクラスを次のように定義します:

template<typename T>
class foo {
public:
    foo(T const& first, T const& second) : first(first), second(second) {}

    template<typename C>
    void bar(C& container, T const& baz) {
        //...
    }
private:
    T first;
    T second;
}

質問は私のバーの機能です...私はそれがコンテナのタイプを定義するために、テンプレート/タイプ名のCの部分を含む理由で、何らかの種類の標準コンテナを使用できるようにする必要があります。 しかし、明らかにそれは正しい方法ではありません。私のテストクラスはそれに不平を言うからです:

エラー: 'bar'はこのスコープで宣言されていません

では、バーの機能を適切に実装するにはどうしたらいいですか? つまり、私のテンプレートクラスの関数として、任意のコンテナ型で...残りのテンプレートクラスはうまく動作します(エラーにならない他の関数を持っています)、それは問題の1つの関数です。

編集:さて、特定の機能(バー)はeraseInRange関数で、指定された範囲内のすべての要素を消去します:

void eraseInRange(C& container, T const& firstElement, T const& secondElement) {...}

それがどのように使用されるかの例は、次のようになります。

eraseInRange(v, 7, 19);

ここで、vはこの場合のベクトルです。

編集2:愚かな私! 私はクラスの外で関数を宣言することになっていました。 とにかく、問題は少し異なりますが、情報は私の元の問題を見つけた後、私はいくつかの他の楽しいエラーを取得したので、私は関数を構築するのに役立ちました。 ありがとう!


テンプレートテンプレートテンプレートのテンプレートにテンプレートを作成します。

template <template <typename, typename...> class Container>
void bar(const Container<T> & c, const T & t)
{
  //
}

C ++ 11を使用していない場合は、バリデーショナルテンプレートを使用することはできません。また、コンテナに必要なテンプレートパラメータをいくつでも指定する必要があります。 たとえば、シーケンスコンテナの場合、2つが必要です。

template <template <typename, typename> class Container, typename Alloc>
void bar(const Container<T, Alloc> & c, const T & t);

または、テンプレートインスタンスであるアロケータのみを許可する場合は、次のようにします。

template <template <typename, typename> class Container, template <typename> class Alloc>
void bar(const Container<T, Alloc<T> > & c, const T & t);

私がコメントで示唆したように、私は個人的にはコンテナ全体をテンプレート化し、それが有効かどうかを確認するために特性を使用することを好みます。 このようなもの:

template <typename Container>
typename std::enable_if<std::is_same<typename Container::value_type, T>::value, void>::type
bar(const Container & c, const T & t);

これは、コンテナがvalue_typeメンバ型を公開するものになりましたので、より柔軟です。 メンバ関数とイテレータをチェックするためのより洗練された特性が考えられます。 たとえば、 かなりのプリンタがそれらのいくつかを実装しています。





containers