2017-12-24 8 views
0

私はすべての算術型のためのstd ::ベクトルを含めるためにハッシュを専門にしようとしているのベクトルのクラスの専門が、それは私が私のように近くに従うことをしようとしたいくつかのエラー特定type_trait

./includes/helpers.hpp:14:22: error: default template argument in a class template partial specialization 
     typename = std::enable_if_t<std::is_arithmetic<dtype>::value> > 
       ^
./includes/helpers.hpp:16:8: error: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used [-Wunusable-partial-specialization] 
struct hash<std::vector<dtype> > 
     ^~~~~~~~~~~~~~~~~~~~~~~~~ 

を投げています異なるenable_if_tガイドを使用することができます。しかし、それは動作していないようだ、私は間違って何ですか?

enable_if_tを使用せずに動作するようです。しかし、その後これは、これまでの私のコードで、このハッシュを使用しないでくださいベクトル

との重複の可能性が存在することになる(以上、「完全」であることをeditted)

#include <iostream> 
#include <type_traits> 
#include <vector> 

namespace std { 

    template <typename dtype, 
       typename = std::enable_if_t< std::is_arithmetic<dtype>::value> > 

    struct hash<std::vector<dtype> > { 
     size_t operator()(const std::vector<dtype> &input) 
     { 
      //perform hash 
     } 
    }; 
} 

using namespace std; 

int main() 
{ 
    const vector<int> i{1,2,3,4}; 
    cout << hash<vector<int>>()(i) << endl; 

    return 0; 
} 
+0

無名のテンプレート引数のデフォルト値として、このような 'enable_if_t'のアプリケーションがあったことはありませんでした。あなたが言及したガイドへのリンクを提供できますか? – Omni

+0

@オムニそれは本当にガイドではありませんが、私はちょうど2つの異なる情報源を使って、うまくいくと思っています。 「名前のないテンプレートのデフォルト値」については、http://en.cppreference.com/w/cpp/types/enable_ifが正しく動作するはずです(5番までスクロールしてください)。 – Michael

+0

[最小限で完全であり、検証可能な例](http://stackoverflow.com/help/mcve)を投稿してください。 –

答えて

1

問題はstd::hashが持っているということであり、 1つのテンプレートパラメータのみで、部分的な特殊化でデフォルトのテンプレートパラメータを追加することはできません。だからあなたはあなたのハッシュで何をしたいかによって、いくつかの選択肢があります。

あなたのアプローチも再考してください。このcomment by Yakkは非常に便利です。

特殊化がユーザー提供のタイプに依存しない限り、テンプレートをstdで特殊化することはできません。 - Yakk

あなたはstdネームスペースに自分自身のhashを入れないで簡単に修正できます。

  1. あなたは、単にテンプレート引数リストからenable_ifを削除して、あなただけのこれまでの算術型のベクトルをハッシュしたいことを考えると、構造体本体内

    static_assert(std::is_arithmetic<dtype>::value, "!"); 
    

    でそれを置き換えることができます。

  2. SFINAEがコールオペレータです。このようにして、同じ構造体内の他のすべてのベクトル型のすべてのハッシュメソッドを提供する必要があります。また、コンパイラを幸せにするために、テンプレートパラメータを繰り返す面白いビジネスを経験しなければなりません。 SFINAE基準が相互に排他的であることは非常に重要です。そうでなければ、恐ろしいエラーが発生します。

    #include <iostream> 
    #include <string> 
    #include <type_traits> 
    #include <vector> 
    
    namespace std { 
    
    template< typename dtype > 
    struct hash< std::vector<dtype> > 
    { 
        template< typename T = dtype > 
        std::enable_if_t<std::is_arithmetic<T>::value, size_t> 
        operator()(const std::vector<T> &input) const 
        { 
         constexpr size_t FNV_prime = 1099511628211ul; 
         constexpr size_t FNV_offset = 14695981039346656037ul; 
    
         size_t hashed = FNV_offset; 
         for(const auto &n:input) 
         { 
          hashed ^= n; 
          hashed *= FNV_prime; 
         } 
         return hashed; 
        } 
    
        template< typename T = dtype > 
        std::enable_if_t<!std::is_arithmetic<T>::value, size_t> 
        operator()(const std::vector<T> &input) const 
        { 
         std::cout << "No hash for you :-(\n"; 
         return 0; 
        } 
    }; 
    
    } // namespace std 
    
    
    int main() { 
        { 
         std::vector<int> v{1,2,3,4}; 
         size_t hash = std::hash<std::vector<int>>{}(v); 
         std::cout << hash << "\n"; 
        } 
    
        { 
         std::vector<std::string> v{"Hello", "world!"}; 
         size_t hash = std::hash<std::vector<std::string>>{}(v); 
         std::cout << hash << "\n"; 
        } 
    } 
    

    Live example

  3. また、ハッシュのための独自の構造体を宣言し、あなたが望む限り多くのテンプレートパラメータを追加することができます。次に、カスタム構造体から継承するにはstd::hashが必要です。

    #include <iostream> 
    #include <string> 
    #include <type_traits> 
    #include <vector> 
    
    template < typename T, bool = std::is_arithmetic<T>::value > 
    struct vector_hash; 
    
    template < typename T> 
    struct vector_hash<T,true> { 
        size_t operator()(std::vector<T> const &input) const 
        { 
         constexpr size_t FNV_prime = 1099511628211ul; 
         constexpr size_t FNV_offset = 14695981039346656037ul; 
    
         size_t hashed = FNV_offset; 
         for(const auto &n:input) 
         { 
          hashed ^= n; 
          hashed *= FNV_prime; 
         } 
         return hashed; 
        } 
    }; 
    
    
    template < typename T> 
    struct vector_hash<T,false> { 
        size_t operator()(std::vector<T> const &) const 
        { 
         std::cout << "No hash for you :-(\n"; 
         return 0; 
        } 
    }; 
    
    namespace std { 
    
    template< typename dtype > 
    struct hash< std::vector<dtype> > : vector_hash<dtype> {}; 
    
    } // namespace std 
    
    
    int main() { 
        { 
         std::vector<int> v{1,2,3,4}; 
         size_t hash = std::hash<std::vector<int>>{}(v); 
         std::cout << hash << "\n"; 
        } 
    
        { 
         std::vector<std::string> v{"Hello", "world!"}; 
         size_t hash = std::hash<std::vector<std::string>>{}(v); 
         std::cout << hash << "\n"; 
        } 
    } 
    

    Live example

+0

@Michael私は継承に基づいて別のメソッドを追加しました。 –

+0

この回答は、成熟したプログラムの作成を助言し、1と2の診断は必要ありません。特殊化がユーザー提供のタイプに依存しない限り、stdでテンプレートを特殊化することはできません。 – Yakk

+0

@HenriMenkeご協力いただきありがとうございます。それはかなり徹底しているので、私はこの問題を見ると少しはっきりしているようです。私はなぜそれがハッシュのために働いていないのだろうかと思っていたが、それは他のシナリオで働いていたことを思い出した。 – Michael

1

ユーザーprovudedタイプでそれを行う場合を除き名前空間stdでテンプレートを特化することは違法です。ベクターはユーザーが提供していません。あなたがする必要がどのような

は次のとおりです。

namespace helper{ 
    template<class T, class=void> 
    struct hash:std::hash<T>{}; 
} 

今あなたがヘルパーのハッシュで通常のsfinaeのトリックを行うことができ、あるいは行き当たりばったり、それを拡張します。

namespace helper { 
    template <typename dtype> 
    struct hash<std::vector<dtype>, 
    std::enable_if_t< std::is_arithmetic<dtype>::value> 
    > { 
    size_t operator()(const std::vector<dtype> &input) const { 
     //perform hash 
    } 
    }; 
} 

std hashの代わりにhelper::hash<T>を渡すだけです。余分なvoidパラメータはsfinaの特殊化を許可し、基本仕様はstdハッシュを転送し、不正な形式の問題はありません。