前提・実現したいこと
可変長テンプレート引数の型がすべて整数型の場合に処理を分けたいです.
引数が1つのときは問題ありませんが,引数が2つ以上になると思い通りの動作になりません.
下記のソースコードではすべて「整数じゃないよ」を出力してしまいます.
今回はパラメータパックをまとめて使いたいので,パラメータを分解せずに済む方法を探しています.
よろしくお願いします.
該当のソースコード
C++
1#include <iostream> 2#include <utility> 3 4template<typename... T, typename std::enable_if<std::is_integral<T...>::value, std::nullptr_t>::type = nullptr> 5void func(T... t) { 6 std::cout << "整数だよ" << std::endl; 7} 8 9template<typename... T> 10void func(T... t) { 11 std::cout << "整数じゃないよ" << std::endl; 12} 13 14int main() { 15 func(10, 10); // 「整数だよ」を出力できるようにしたい. 16 func(10, 3.14); // 未定義動作.できればエラーにしたい. 17 func(3.14, 3.14); // 「整数じゃないよ」を出力. 18 return 0; 19}
補足情報(FW/ツールのバージョンなど)
C++11
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/02/01 07:11
回答4件
0
ベストアンサー
質問文中のコードは std::is_integral
に複数の型引数を渡そうとしていますが、ひとつの型しか受け取れないからです。 なのでその候補は常に失敗し、もう一方の候補が常に選ばれるという動作になります。 (常に展開に失敗するテンプレートであって、テンプレート自体はエラーではありません。 これは展開フェイズに関するややこしいルールが関係するのでここでは説明しません。)
C++17 からは fold expression が使えるのでもう少し簡単なのですが、 C++11 の範囲内でやるとなると再帰的なテンプレートの展開は必要です。 分解せずにやる方法はありません。
分解して判定する処理をする処理をトレイツにまとめることはできます。
#include <iostream> #include <type_traits> template<class ...T> class is_integral_all { private: template<class U, class... W> struct helper { static constexpr std::size_t value = std::is_integral<U>::value + helper<W...>::value; }; template<class U> struct helper<U> { static constexpr std::size_t value = std::is_integral<U>::value; }; public: static constexpr bool value = helper<T...>::value == sizeof...(T); }; template<typename... T, typename std::enable_if<is_integral_all<T...>::value, std::nullptr_t>::type = nullptr> void func(T...) { std::cout << "整数だよ" << std::endl; } template<typename... T, typename std::enable_if<not is_integral_all<T...>::value, std::nullptr_t>::type = nullptr> void func(T...) { std::cout << "整数じゃないよ" << std::endl; } int main() { func(10, 10); // 「整数だよ」を出力できるようにしたい. func(10, 3.14); // 未定義動作.できればエラーにしたい. func(3.14, 3.14); // 「整数じゃないよ」を出力. return 0; }
ここではコードを簡潔に理解しやすくするため、型が違うものが混ざっている場合をエラーとして弾いていませんが、仕組みを理解すれば簡単だと思うので演習の題材にでもしてください。
投稿2020/02/01 08:27
総合スコア5437
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/02/01 11:06
2020/02/01 12:08
2020/02/01 13:03
2020/02/01 13:49 編集
2020/02/02 05:12 編集
2020/02/02 08:26
2020/02/03 00:32
2020/02/07 12:49 編集
2020/02/08 05:32
2020/02/08 12:25 編集
2020/02/09 06:04
2020/02/09 06:22
2020/02/09 06:59
0
SaitoAtsushi さんの回答の補足です。
回答にあるように、C++11 とその標準ライブラリの範囲内では処理の自作が必要です。ただし、基本的な処理は Boost C++ Libraries にある可能性があります。
質問の場合、Boost.MPL に、std::is_integral
のような bool 値を返すメタ関数の論理積(&&)を計算するメタ関数として boost::mpl::and_
、論理否定(!)を計算するメタ関数として、boost::mpl::not_
があります。これら使うと、次のように書けます。
cpp
1#include <iostream> 2#include <utility> 3#include <boost/mpl/and.hpp> 4#include <boost/mpl/not.hpp> 5 6// static_assert が常に評価されることを防ぐ 7template <typename... T> 8struct dependent_false { static constexpr bool value = false; }; 9 10// オーバーロードの優先順位の制御のため 11template <int I> struct rank : rank<I-1> {}; 12template <> struct rank<0> {}; 13 14template< 15 typename... T, 16 typename std::enable_if< 17 boost::mpl::and_<std::is_integral<T>...>::value, // すべて整数のとき true 18 std::nullptr_t 19 >::type = nullptr 20> 21void func_impl(rank<1>, T...) { 22 std::cout << "整数だよ" << std::endl; 23} 24 25template< 26 typename... T, 27 typename std::enable_if< 28 // すべて整数ではないとき true 29 boost::mpl::and_<boost::mpl::not_<std::is_integral<T>>...>::value, 30 std::nullptr_t 31 >::type = nullptr 32> 33void func_impl(rank<1>, T...) { 34 std::cout << "整数じゃないよ" << std::endl; 35} 36 37template<typename... T> 38void func_impl(rank<0>, T...) { 39 static_assert(dependent_false<T...>::value, "Error!"); 40} 41 42template <typename... T> 43void func(T&&... t) { 44 func_impl(rank<1>{}, std::forward<T>(t)...); 45}
補足:
C++17 では、<type_traits> に同様のメタ関数が用意されています。boost::mpl::and_
に対応するものとして std::conjunction
、boost::mpl::not_
に対応するものとして std::negation
があります。
投稿2020/02/01 08:55
編集2020/02/01 10:05総合スコア124
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
SaitoAtsushi様と同じく(2)の例でエラーにはなりませんが、all_of(型版)のような汎用的なテンプレートを作成しライブラリ化しておくと、同じようなケースすべてに対して適応させることができます。
Cpp
1#include <iostream> 2#include <tuple> 3#include <type_traits> 4 5namespace detail { 6 template <template <class> class Pred, class List> 7 struct all_of_impl; 8 9 template <template <class> class Pred, 10 class Head, class... Tail> 11 struct all_of_impl<Pred, std::tuple<Head, Tail...>> { 12 static const bool value = 13 Pred<Head>::value ? 14 all_of_impl<Pred, std::tuple<Tail...>>::value : 15 false; 16 }; 17 18 template <template <class> class Pred> 19 struct all_of_impl<Pred, std::tuple<>> { 20 static const bool value = true; 21 }; 22} // namespace detail 23 24// all_of 25template <template <class> class Pred, class... List> 26struct all_of { 27 static const bool value = detail::all_of_impl<Pred, std::tuple<List...>>::value; 28}; 29 30// 目的の関数 31template <typename... T, 32 typename std::enable_if<all_of<std::is_integral, T...>::value, std::nullptr_t>::type = nullptr> 33void func(T... t) { 34 std::cout << "整数だよ" << std::endl; 35} 36 37template <typename... T, 38 typename std::enable_if<not all_of<std::is_integral, T...>::value, std::nullptr_t>::type = nullptr> 39void func(T... t) { 40 std::cout << "整数じゃないよ" << std::endl; 41} 42 43int main() { 44 func(10, 10); // 整数だよ(1) 45 func(10, 3.14); // 整数じゃないよ(2) 46 func(3.14, 3.14); // 整数じゃないよ(3) 47 48 return 0; 49}
投稿2020/02/01 17:09
総合スコア69
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
こんにちは。
この辺は慣れですね。このくらいの難易度なら私でもなんとかなります。
C++
1#include <iostream> 2#include <utility> 3 4template<typename T, typename std::enable_if<std::is_integral<T>::value, std::nullptr_t>::type = nullptr> 5void func(T t) 6{ 7 std::cout << "整数だよ" << std::endl; 8} 9 10template<typename T, typename std::enable_if<!std::is_integral<T>::value, std::nullptr_t>::type = nullptr> 11void func(T t) 12{ 13 std::cout << "整数じゃないよ" << std::endl; 14} 15 16template<typename F, typename S, typename... T> 17void func(F f, S s, T... t) 18{ 19 static_assert(std::is_same<F, S>::value, "Error!"); 20 func(s, t...); 21} 22 23int main() { 24 func(10, 10); // 「整数だよ」を出力できるようにしたい. 25 func(10, 3.14); // 未定義動作.できればエラーにしたい. 26 func(3.14, 3.14); // 「整数じゃないよ」を出力. 27 return 0; 28}
投稿2020/02/01 07:31
総合スコア23272
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。