2017-07-07 3 views
8

std::is_integralに応じていくつかのタイプしか受け付けない関数オブジェクトを持つライブラリがあります。私は条件が失敗したときにstd::is_invocablefalseに返すようにしたいが、ユーザーが関数オブジェクトのインスタンスを呼び出そうとしたときにもエラーメッセージstatic_assertが欲しい。このような実装ではstatic_assertとstd :: is_invocableのベストを得る

struct function 
{ 
    template<typename Iterator> 
    auto operator()(Iterator first, Iterator last) const 
     -> std::enable_if_t<std::is_integral_v< 
      typename std::iterator_traits<Iterator>::value_type 
     >> 
    { /* something */ } 
}; 

SFINAE条件が満たされないときstd::is_invocableは予想通りstd::false_typeですが、彼らは呼び出ししようとすると、ユーザーが醜いSFINAEエラーメッセージが発生します。ここに私は現在持っている関数オブジェクトの簡略化した例でありますSFINAE条件を満たさないパラメータを持つ関数オブジェクト。

この実装では
struct function 
{ 
    template<typename Iterator> 
    auto operator()(Iterator first, Iterator last) const 
     -> void 
    { 
     static_assert(std::is_integral_v<typename std::iterator_traits<Iterator>::value_type>, 
         "function can only be called with a collection of integers"); 

     /* something */ 
    } 
}; 

、ユーザーがオリジナルのSFINAE条件が満たされていないフレンドリーエラーメッセージを得るが、かどうかを尋ねられたときstd::is_invocablestd::true_typeです:

より良いエラーメッセージを取得するには、私の代わりに、以下のソリューションを試してみましたfunctionインスタンスは、std::is_integralを満たさないタイプを処理できます。

私はいくつかのトリックやdecltype(auto)を含むバリエーション、if constexprや他のメカニズムを試してみましたが、エラーメッセージが素晴らしく、どこfunctionが正しくないタイプで呼び出すことができるかどうかを尋ねるときstd::is_invocableが期待std::false_typeに対応したクラスを取得できませんでした。

私はここで何が欠けていますか? std::is_invocableユーザーフレンドリーなエラーメッセージの両方を得る方法はありますか?

+0

私は最近これについてブログの投稿を書いています。https://gracicot.github.io/tricks/2017/07/01/deleted-function-diagnostic.html –

答えて

7

ここに1つのひどい方法があります。オーバーロードを追加:contrained関数テンプレートは、より専門的で好ましいので、

template <typename Iterator> 
auto operator()(Iterator first, Iterator last) const 
    -> std::enable_if_t<std::is_integral_v< 
     typename std::iterator_traits<Iterator>::value_type 
    >>; 

template <typename Iterator, class... Args> 
void operator()(Iterator, Iterator, Args&&...) const = delete; // must be integral 

これは、is_invocable<>条件を満たして - それは削除されます他、その条件が満たされた場合、この関数は呼び出し可能です。

あなたはそれを呼び出すようにしようとした場合、あなたが得るので、これは、エラーの場合には少し良くない:

foo.cxx: In function ‘int main()’: 
foo.cxx:31:13: error: use of deleted function ‘void function::operator()(Iterator, Iterator, Args&& ...) const [with Iterator = std::__cxx11::basic_string<char>*; Args = {}]’ 
    f(&i, &i); 
      ^
foo.cxx:19:10: note: declared here 
    void operator()(Iterator, Iterator, Args&&...) const = delete; // must be integral 
      ^~~~~~~~ 

コメントはソートの静的アサートのようなものですエラーメッセージ、に表示メッセージ?


これは、実際にはConceptsの動機の1つです。 requires代わりのenable_ifで、我々が得る:

foo.cxx: In function ‘int main()’: 
foo.cxx:26:13: error: no match for call to ‘(function) (std::__cxx11::string*, std::__cxx11::string*)’ 
    f(&i, &i); 
      ^
foo.cxx:11:10: note: candidate: void function::operator()(Iterator, Iterator) const requires is_integral_v<typename std::iterator_traits<_Iter>::value_type> [with Iterator = std::__cxx11::basic_string<char>*] 
    void operator()(Iterator first, Iterator last) const 
      ^~~~~~~~ 
foo.cxx:11:10: note: constraints not satisfied 
foo.cxx:11:10: note: ‘is_integral_v<typename std::iterator_traits<_Iter>::value_type>’ evaluated to false 

...少し良く私は推測します。

+0

コンセプトソリューションは実際には少し上手です。私はSFINAEの振る舞いが必要なときには別の場所がありますが、 '= delete'が私の場合に実行可能かどうかを確認する必要がありますが、それは探索する興味深いパスかもしれません。 – Morwenn

+4

'= delete'トリックを改善した[記事](https://gracicot.github.io/tricks/2017/07/01/deleted-function-diagnostic.html)が見つかったので、専用の' static_assert'メッセージですが、これは悪くありません。 – Morwenn

+0

@Morwenn記事の作者が正しいかどうか疑問に思います。私はあなたが読んだ**有効な特殊化がないテンプレートを作成することはできません。その規則を破って、診断さえせずにコードを整形しないようにしてください。 –

関連する問題