質問をすることでしか得られない、回答やアドバイスがある。

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

ただいまの
回答率

88.80%

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

解決済

回答 3

投稿

  • 評価
  • クリップ 1
  • VIEW 952

ttact

score 29

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

namespace
{
    template <typename T>
    struct has_size_helper
    {
    private:
        template <typename U> static auto test(int)
            -> decltype(std::declval<U&>().size(), std::true_type());
        template <typename U> static auto test(...)
            -> decltype(std::false_type());
    public:
        using type = decltype(test<T>(0));
    };
}
template <typename T> struct has_size
    : has_size_helper<std::remove_reference_t<T>>::type {};
template <typename T> constexpr bool has_size_v = has_size<T>::value;


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

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


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

template <typename T>
struct Holder
{
    T entity_;
    operator T const&() const { return entity_; }
};


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

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

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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+2

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

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


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

template <typename T>
struct Holder2
{
    using value_type = T;
    T entity_;
    operator T const&() const { return entity_; }
};

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

#include <type_traits>

namespace {

template<class, class = void>
struct has_size : std::false_type {};

template<class T>
struct has_size<T, std::void_t<
  decltype(std::declval<T>().size())>
> : std::true_type {};

template<class, class = void>
struct convertible_to_value_type : std::false_type {};

template<class T>
struct convertible_to_value_type<T, std::void_t<
   decltype(std::declval<typename T::value_type>() = std::declval<T>())>
> : std::true_type {};
}

template <
  typename T,
  std::enable_if_t<has_size<T>::value, std::nullptr_t> = nullptr
>
bool f(T&& t)
{
  return t.size() != 0;
}

template <
  typename T,
  std::enable_if_t<
    convertible_to_value_type<T>::value && has_size<typename T::value_type>::value,
    std::nullptr_t> = nullptr
>
bool f(T&& t)
{
  return static_cast<typename T::value_type>(t).size() != 0;
}


struct S1 {
  unsigned size() const { return 42; }
};
struct S2 {};

template <typename T>
struct Holder2
{
  using value_type = T;
  T entity_;
  operator T const&() const { return entity_; }
};

int main()
{
  f(S1{});
  f(Holder2<S1>{});
}

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+2

こんにちは。

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

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

template<typename... Ts>
struct CanCast : public std::false_type
{
};

// ここでhas_sizeをうまいこと定義する。


// これより前のどこかで Holder<T>を定義する。
// なので、std::vectorなどにも適用できる筈です。
template<typename T>
struct CanCast<Holder<T>> : public std::true_type
{
    typedef T CastableClass; 
};


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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

Chironian様、yohhoy様

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

template <typename T>
struct castable_tag
    : std::false_type
{
};

template <typename T>
auto f(T&&,
    std::enable_if_t<has_size_v<std::remove_const_t<T>>>* = nullptr)
{
    // 実処理...
}

template <typename T>
auto f(T&& value,
    std::enable_if_t<
        std::conjunction_v<
            std::negation<has_size<std::remove_const_t<T>>>,
            castable_tag<std::remove_const_t<T>>
        >
    >* = nullptr)
{
    return f(static_cast<typename castable_tag<std::remove_const_t<T>>::to_type>(value));
}

template <typename T>
struct Holder { /*前回と同じなので略*/ };

template <typename T>
struct castable_tag<Holder<T>>
    : std::true_type
{
    using to_type = T const&;
};


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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 88.80%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る