2

次の構造のコードがあります。

template <typename T>
struct Foo
{
  struct Bar
  {
    int data;
  };
};

型が Foo か Bar かを教えてくれるメタ関数を書きたいと思っています。最初のものは簡単です:

template <typename T>
struct is_foo : boost::mpl::false_
{};

template <typename T>
struct is_foo<Foo<T> > : boost::mpl::true_
{};
...
BOOST_MPL_ASSERT(( is_foo<Foo<int> > ));
BOOST_MPL_ASSERT_NOT(( is_foo<int> ));

ただし、同じアプローチは Bar では機能しません。

template <typename T>
struct is_bar : boost::mpl::false_
{};

template <typename T>
struct is_bar<typename Foo<T>::Bar> : boost::mpl::true_
{};

このコードはコンパイラによって拒否されます。GCC 言います:

main.cpp:38:8: error: template parameters not used in partial specialization:
main.cpp:38:8: error:         ‘T’

奇妙なことに、clang はコードをコンパイルしますが、警告を発行し、メタ関数は機能しません (常に false):

main.cpp:38:8: warning: class template partial specialization contains a template parameter that can not be deduced;
      this partial specialization will never be used
struct is_bar<typename Foo<T>::Bar> : boost::mpl::true_
       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:37:20: note: non-deducible template parameter 'T'
template <typename T>
                   ^

この問題の回避策はありますか? C++11 固有のソリューションで問題ありません。

4

3 に答える 3

1

問題は、それが型の名前Tの一部であるが、型の構造の一部ではないということです。Foo<T>::Bar

T考えられる解決策は、次のタイプの構造でエンコードすることです。

template<typename Outer, typename Inner> struct Nested: public Inner {
  using Inner::Inner;
};
template<typename T> struct Foo {
  struct BarImpl {
    int data;
  };
  using Bar = Nested<Foo<T>, BarImpl>;
};

template <typename T> struct is_bar: std::false_type {};
template <typename T, typename U> struct is_bar<Nested<Foo<T>, U>>:
  std::is_same<typename Foo<T>::Bar, Nested<Foo<T>, U>> {};

テスト:

static_assert(is_bar<Foo<int>::Bar>::value, "!");
static_assert(!is_bar<Foo<int>>::value, "!");
static_assert(!is_bar<int>::value, "!");
于 2012-12-04T18:36:06.640 に答える
1

これは、TTI(http://svn.boost.org/svn/boost/sandbox/tti)を使用した、私自身の質問に対する恐ろしく洗練されていない解決策です。

まず、Bar にダミー タグを追加します。

template <typename T>
struct Foo
{
  struct Bar
  {
    typedef void i_am_bar;
    int data;
  };
};

次に、TTI を使用してそのタグを確認します。

BOOST_TTI_HAS_TYPE(i_am_bar);

template <typename T>
struct is_bar : boost::tti::has_type_i_am_bar<T>
{};
...
BOOST_MPL_ASSERT(( is_bar<Foo<int>::Bar> ));
BOOST_MPL_ASSERT_NOT(( is_bar<Foo<int> > ));
BOOST_MPL_ASSERT_NOT(( is_bar<int> ));

確かにラッキーですが、それは私のユースケースを満たしています。

于 2012-12-05T02:39:07.307 に答える
0

コンパイラは正しいです。単純でわかりやすい説明は次のとおりです。指定されたテンプレート内にネストされた型バーがあることを認識するためだけに、可能なすべての型を置き換えたくないだけですT。より正確な説明は、テンプレートに関する「古典的な」(そしてよく知られていることを願っています)本「C++ Templates - The Complete Guide」にあります。

幸いなことに、C++11 はそれをさらに改善するのに役立ちます! :)

#include <type_traits>

template <typename T>
struct has_nested_bar
{
    template <typename W>
    struct wrapper {};

    template <typename C>
    static std::true_type check(
        const wrapper<C>*
      , const typename C::Bar* = nullptr
      );

    template <class C>
    static std::false_type check(...);

    constexpr static bool value = std::is_same<
        decltype(check<T>(nullptr))
      , std::true_type
      >::type::value;
    typedef std::integral_constant<bool, value> type;
};

このメタ関数は、特定の型にネストされた型があるかどうかを確認しますBar(私が理解している限り、それはあなたの最初の目的でしたis_bar)。

template <typename T>
struct Foo
{
    struct Bar
    {
        int data;
    };
};

struct Bar {};

int main()
{
    std::cout << has_nested_bar<Foo<int>>::value << std::endl;
    std::cout << has_nested_bar<Bar>::value << std::endl;
    return 0;
}

出力します:

zaufi@gentop /work/tests $ ./has-nested-bar
1
0

後で、このメタ関数をあなたと組み合わせてis_foo、ネストされたものが実際に...Barの内部にあることを確認できます。Foo

于 2012-12-04T20:43:34.387 に答える