2016-03-23 6 views
2

ユーザーがこれらの型でインスタンス化することを期待するいくつかの型とテンプレート関数を提供するライブラリを設計しているとします。テンプレート関数のシグネチャは、テンプレートパラメータの中にネストされた型定義に依存し、私はテンプレートが互換性のない型でインスタンス化された場合に素敵なエラーメッセージを与えることstatic_assertを使用する:これは、通りに動作しない関数シグネチャが無効な場合でもstatic_assertをトリガーする

// library.h 
#include <type_traits> 

struct compatible_with_f {}; 

struct foo : compatible_with_f { 
    using some_type = int; 
}; 

struct bar : compatible_with_f { 
    using some_type = float; 
}; 

template <typename T> 
void f(typename T::some_type param) { 
    static_assert(std::is_base_of<compatible_with_f, T>::value, 
       "the template parameter to f should be `foo` or `bar`"); 
    // some code, which also uses the type T itself directly 
} 

// main.cc 

int main() { 
    f<foo>(0); // works 
    f<bar>(0.f); // works 
    f<int>(0); // error: no matching function for call to 'f' 
} 

私の希望:私の友好的なエラーメッセージは、fの署名が意味をなさないので、印刷されないので、コンパイラはそれが機能の本体のstatic_assertに到達する前に壊れてしまいます。

私が好きなやり方を実現する方法はありますか?ここで私が思い付くことが最高です:

// library.h 
#include <type_traits> 

struct compatible_with_f {}; 

struct foo : compatible_with_f { 
    using some_type = int; 
}; 

struct anything { 
    template <typename T> 
    anything(T&&) {} 
}; 

template <typename, typename = void> 
struct get_some_type { 
    using type = anything; 
}; 

template <typename T> 
struct get_some_type<T, typename std::enable_if<std::is_base_of<compatible_with_f, T>::value>::type> { 
    using type = typename T::some_type; 
}; 

template <typename T> 
void f(typename get_some_type<T>::type param) { 
    static_assert(std::is_base_of<compatible_with_f, T>::value, 
       "the template parameter to f should be `foo` or `bar`"); 
    // some code, which also uses the type T itself directly 
} 

// main.cc 

int main() { 
    f<foo>(0); // works 
    f<int>(0); // error: the template parameter to f should be `foo` or `bar` 
} 

私は何から変換可能なデフォルトsome_typeを提供するために、SFINAEを使用しています。これで署名は常に意味をなさないので、コンパイラは関数の本体をインスタンス化していいエラーメッセージを出します。しかし、私はこの解決策が嫌いです。複雑すぎるようですが、fの署名がはるかに明確でないという大きな欠点があります。T::some_typeになる前に、奇妙な魔法がかかりました。より良い方法がありますか?あなたが実際の実装に渡す前にチェックを行うには転送機能を書くことができ

答えて

0

template <typename T> 
void f_impl(typename T::some_type param) { 
    // some code, which also uses the type T itself directly 
} 

template<typename U, typename T> 
void f(T&& t) 
{ 
    static_assert(std::is_base_of<compatible_with_f, U>::value, 
       "the template parameter to f should be `foo` or `bar`"); 
    f_impl<U>(std::forward<T>(t)); 
} 

LIVE

EDIT:また

、あなたがのためにあまり専門的な過負荷を追加することができますf

template <typename T> 
void f(T param) { 
    static_assert(std::is_base_of<compatible_with_f, T>::value, 
       "the template parameter to f should be `foo` or `bar`"); 
} 

template <typename T> 
void f(typename T::some_type param) { 
    static_assert(std::is_base_of<compatible_with_f, T>::value, 
       "the template parameter to f should be `foo` or `bar`"); 
    // some code, which also uses the type T itself directly 
} 

LIVE

+0

私の提案された解決策の最大の欠点は、公開されるAPI機能の署名があまり明確でないことでした。これはそれよりもはるかに明確ではないようです。 – STU

+0

@STU少なくとも、それは簡単です:)また、署名をクリアに保ち、SFINAEによって設定された解像度から機能が削除されないようにすることはできません。 – Rostislav

+0

@STUああ待ってください。私の編集を参照してください。 – Rostislav

関連する問題