8

前の質問のフォローアップとして、明示的な特殊化を必要とするテンプレート関数の存在を検出しようとしています。

私の現在の作業コードは、非テンプレート関数を検出します (DyP の助けのおかげで)。依存する名前のルックアップを使用できるように少なくとも 1 つのパラメーターを取る場合:

// switch to 0 to test the other case
#define ENABLE_FOO_BAR 1

namespace foo {
  #if ENABLE_FOO_BAR
    int bar(int);
  #endif
}

namespace feature_test {
  namespace detail {
    using namespace foo;
    template<typename T> decltype(bar(std::declval<T>())) test(int);
    template<typename> void test(...);
  }
  static constexpr bool has_foo_bar = std::is_same<decltype(detail::test<int>(0)), int>::value;
  static_assert(has_foo_bar == ENABLE_FOO_BAR, "something went wrong");
}

(ENABLE_FOO_BARマクロはテスト目的のためのものです。私の実際のコードでは、そのようなマクロを利用できません。そうでなければ、SFINAE を使用しません)

これは、テンプレート引数がコンパイラによって自動的に推定できる場合、テンプレート関数でも完全に機能します。

namespace foo {
  #if ENABLE_FOO_BAR
    template<typename T> int bar(T);
  #endif
}

ただし、明示的な特殊化が必要なテンプレート関数を検出しようとすると、存在するstatic_assert場合foo::bar()開始されます:

namespace foo {
  #if ENABLE_FOO_BAR
    template<typename T, typename U> T bar(U);
  #endif
}

//...
// error: static assertion failed: something went wrong

明らかに、コンパイラはテンプレート引数を推測できないbar()ため、検出は失敗します。呼び出しを明示的に特殊化して修正しようとしました:

template<typename T> decltype(bar<int, T>(std::declval<T>())) test(int);
//      explicit specialization  ^^^^^^^^

これは、存在する場合は正常に機能します (関数は正しく検出されます) が、存在しない場合foo::bar()はすべてが崩壊します。foo::bar()

error: ‘bar’ was not declared in this scope
     template<typename T> decltype(bar<int, T>(std::declval<T>())) test(int);
                                   ^
error: expected primary-expression before ‘int’
     template<typename T> decltype(bar<int, T>(std::declval<T>())) test(int);
                                       ^
// lots of meaningless errors that derive from the first two

barコンパイラはそれがテンプレートであることを認識していないため、明示的な特殊化の試みが失敗したようです。

これを修正するために私が試みたすべてのことは割愛して、ポイントに直行します:インスタンス化するために明示的な特殊化を必要とするような関数の存在をどのように検出できますか?template<typename T, typename U> T bar(U);

4

1 に答える 1

2

以下が役立つ場合があります。

// Helper macro to create traits to check if function exist.
// Note: template funcName should exist, see below for a work around.
#define HAS_TEMPLATED_FUNC(traitsName, funcName, Prototype)                          \
    template<typename U>                                                             \
    class traitsName                                                                 \
    {                                                                                \
        typedef std::uint8_t yes;                                                    \
        typedef std::uint16_t no;                                                    \
        template <typename T, T> struct type_check;                                  \
        template <typename T = U> static yes &chk(type_check<Prototype, &funcName>*); \
        template <typename > static no &chk(...);                                    \
    public:                                                                          \
        static bool const value = sizeof(chk<U>(0)) == sizeof(yes);                  \
    }

したがって、提供された名前空間barの有無にかかわらずbar2

// namespace to test
namespace foo {
    template<typename T, typename U> T bar(U);
    // bar2 not present
}

bar<int, int>との存在をチェックするコードbar2<int, int>

// dummy class which should be never used
namespace detail {
    struct dummy;
}

// Trick, so the names exist.
// we use a specialization which should never happen
namespace foo {
    template <typename T, typename U>
    std::enable_if<std::is_same<detail::dummy, T>::value, T> bar(U);

    template <typename T, typename U>
    std::enable_if<std::is_same<detail::dummy, T>::value, T> bar2(U);
}

#define COMMA_ , // trick to be able to use ',' in macro

// Create the traits
HAS_TEMPLATED_FUNC(has_foo_bar, foo::bar<T COMMA_ int>, int(*)(int));
HAS_TEMPLATED_FUNC(has_foo_bar2, foo::bar2<T COMMA_ int>, int(*)(int));

// test them
static_assert(has_foo_bar<int>::value, "something went wrong");
static_assert(!has_foo_bar2<int>::value, "something went wrong");
于 2013-12-30T01:20:00.797 に答える