🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
メタプログラミング

メタプログラミングとは、プログラミング技法の一つ。プログラムをプログラミングすることを指します。他のプログラムや、そのプログラム自体を操作・出力するメタプログラムを記述する作業をメタプログラミングと呼びます。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

3回答

4359閲覧

[C++17]あるメンバ関数を持つ型にキャスト可能か調べるメタ関数

ttact

総合スコア170

メタプログラミング

メタプログラミングとは、プログラミング技法の一つ。プログラムをプログラミングすることを指します。他のプログラムや、そのプログラム自体を操作・出力するメタプログラムを記述する作業をメタプログラミングと呼びます。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

1グッド

1クリップ

投稿2019/10/19 05:56

size()メンバ関数を保持するかどうか調べるメタ関数has_sizeを作ります。

C++

1namespace 2{ 3 template <typename T> 4 struct has_size_helper 5 { 6 private: 7 template <typename U> static auto test(int) 8 -> decltype(std::declval<U&>().size(), std::true_type()); 9 template <typename U> static auto test(...) 10 -> decltype(std::false_type()); 11 public: 12 using type = decltype(test<T>(0)); 13 }; 14} 15template <typename T> struct has_size 16 : has_size_helper<std::remove_reference_t<T>>::type {}; 17template <typename T> constexpr bool has_size_v = has_size<T>::value;

これを使って型判定を行っていました。

C++

1// size()メンバ関数を持っているオブジェクトにだけ呼び出し可能。 2template <typename T> 3bool f(T&&, std::enable_if_t<has_size_v<T>>* = nullptr) { ... }

上記を前提として...
こんなクラスに出会いました。

C++

1template <typename T> 2struct Holder 3{ 4 T entity_; 5 operator T const&() const { return entity_; } 6};

HolderのTにsize()メンバ関数を持つクラスを指定された状態では、f(Holder<T>())は当然SFINAEで候補から落とされてしまいます。
しかしf()内の実装は、Holder<T>をTにキャストさえできれば問題なく動作するのです。

そしてTやHolderは後から追加される任意の型でも問題ないようにしたいと思います。なので各型に対してオーバーロードしたり特殊化したりはしなくてすむようにしたいです。

上記の条件で、f()がHolder<T>を受け付けられるようにできるものでしょうか?
キャスト先の型を直接指定できない時点で、私の知識では無理なのですが...。

yumetodo👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

キャスト先の型を直接指定できない時点で、私の知識では無理なのですが...。

おっしゃる通り、インスタンス化されたテンプレートクラスHolder<SomeType>型の外部から、そのテンプレート実引数SomeType型を取り出すことはできません。つまり、その要件そのままでは実装不可能です。


一般的な設計としては、テンプレートクラスのテンプレート実引数に対応する型名(value_typeなど)を公開します。このようなインタフェース設計を前提とすれば、Holder2<SomeType>型からHolder2<SomeType>::value_typeつまりSomeType型を取り出せるようになります。

c++

1template <typename T> 2struct Holder2 3{ 4 using value_type = T; 5 T entity_; 6 operator T const&() const { return entity_; } 7};

実装例:https://wandbox.org/permlink/3xJkyzhRqL1As2rR

c++

1#include <type_traits> 2 3namespace { 4 5template<class, class = void> 6struct has_size : std::false_type {}; 7 8template<class T> 9struct has_size<T, std::void_t< 10 decltype(std::declval<T>().size())> 11> : std::true_type {}; 12 13template<class, class = void> 14struct convertible_to_value_type : std::false_type {}; 15 16template<class T> 17struct convertible_to_value_type<T, std::void_t< 18 decltype(std::declval<typename T::value_type>() = std::declval<T>())> 19> : std::true_type {}; 20} 21 22template < 23 typename T, 24 std::enable_if_t<has_size<T>::value, std::nullptr_t> = nullptr 25> 26bool f(T&& t) 27{ 28 return t.size() != 0; 29} 30 31template < 32 typename T, 33 std::enable_if_t< 34 convertible_to_value_type<T>::value && has_size<typename T::value_type>::value, 35 std::nullptr_t> = nullptr 36> 37bool f(T&& t) 38{ 39 return static_cast<typename T::value_type>(t).size() != 0; 40} 41 42 43struct S1 { 44 unsigned size() const { return 42; } 45}; 46struct S2 {}; 47 48template <typename T> 49struct Holder2 50{ 51 using value_type = T; 52 T entity_; 53 operator T const&() const { return entity_; } 54}; 55 56int main() 57{ 58 f(S1{}); 59 f(Holder2<S1>{}); 60}

おまけ:C++20のコンセプトを利用できるとすっきりします https://wandbox.org/permlink/phnyEYpsTWhbQtl9

投稿2019/10/19 13:14

編集2019/10/19 13:29
yohhoy

総合スコア6191

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

こんにちは。

Holder<T>がTへキャスト可能であることを、Holder<T>の定義側でhas_sizeへ教える術があれば可能ですね。

あまり自信はないのですが、下記のような部分特殊化テンプレートをうまいこと定義できればできそうな気もします。

C++

1template<typename... Ts> 2struct CanCast : public std::false_type 3{ 4}; 5 6// ここでhas_sizeをうまいこと定義する。 7 8 9// これより前のどこかで Holder<T>を定義する。 10// なので、std::vectorなどにも適用できる筈です。 11template<typename T> 12struct CanCast<Holder<T>> : public std::true_type 13{ 14 typedef T CastableClass; 15};

なお、この記述ではコンパイルさえできない予感がヒシヒシとします。TMPはなかなかハードなので。

has_sizeの定義より前でcanCastのfalseになるプライマリー・テンプレートを定義し、has_sizeの実体化より前にtrueとなるcanCastの部分特殊化を定義してhas_sizeの実体化へHolder<T>をT(CastableClass)へキャストできることを教えるというアイデアとして捉えて下さい。

投稿2019/10/19 07:34

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

Chironian様、yohhoy様

ご回答ありがとうございます。
頂いたヒントに基づいて、以下のように実装できました。ありがとうございます!

C++

1template <typename T> 2struct castable_tag 3 : std::false_type 4{ 5}; 6 7template <typename T> 8auto f(T&&, 9 std::enable_if_t<has_size_v<std::remove_const_t<T>>>* = nullptr) 10{ 11 // 実処理... 12} 13 14template <typename T> 15auto f(T&& value, 16 std::enable_if_t< 17 std::conjunction_v< 18 std::negation<has_size<std::remove_const_t<T>>>, 19 castable_tag<std::remove_const_t<T>> 20 > 21 >* = nullptr) 22{ 23 return f(static_cast<typename castable_tag<std::remove_const_t<T>>::to_type>(value)); 24} 25 26template <typename T> 27struct Holder { /*前回と同じなので略*/ }; 28 29template <typename T> 30struct castable_tag<Holder<T>> 31 : std::true_type 32{ 33 using to_type = T const&; 34};

静的リフレクションが導入されたら、castable_tag無しでなんとかならないかな~と思います。。。

投稿2019/10/23 00:00

ttact

総合スコア170

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問