我正在尝试构建一个特征,用于检查是否存在嵌套的模板化类。我需要检查一个类O
是否有一个带有模板参数inner
的嵌套类T
:
template <typename O, typename T> struct has_inner {
static const bool value = std::is_class<typename O::template inner<T> >::value;
};
但是,这不能正常工作。给出了两个示例类dummy
和ok
struct dummy {};
struct ok {
template <typename T>
struct inner {
};
};
检查ok
std::cout << std::boolalpha << has_inner<ok, float>::value << std::endl;
会有效,而对dummy
std::cout << std::boolalpha << has_inner<dummy, int>::value << std::endl;
将无法使用错误
在clang 3.2上编译error: 'inner' following the 'template' keyword does not refer to a template
static const bool value = std::is_class<typename O::template inner<T> >::value;
^~~~~
note: in instantiation of template class 'has_inner<dummy, int>' requested here
std::cout << std::boolalpha << has_inner<dummy, int>::value << std::endl;
似乎编译器在将模板化表达式传递给std::is_class
之前尝试实际形成该模板化表达式。因此,我看到两个解决方案:
但是,我不知道如何表演,有人可以帮忙吗?
答案 0 :(得分:3)
您通常使用SFINAE来实现这样的特征,这些特征是您的实施无法利用的。
如上所述,编译器将尝试实例化typename O::template inner<T>
,无论它是否可能;如果不可能,编译器将在您的脸上抛出错误诊断。
您需要做的是进行条件检查,以查看T
内部是否实际上有模板类,如果它没有实例化,则无需实例化。
实现可能如下面的代码段所示,接下来会有解释。
namespace impl {
template<class T, class... Args>
struct has_inner {
template<class U, typename = typename U::template inner<Args...>> // (A)
static std::true_type test (int);
template<class U>
static std::false_type test (...); // (B)
using result_type = decltype (test<T> (0)); // (C)
};
}
template<class... Ts>
using has_inner = typename impl::has_inner<Ts...>::result_type;
注意:使用
decltype(test<T>(0))
,我们将拥有 std :: true_type 或 std :: false_type 这是处理 type-traits 结果时的标准行为。
SFINAE 的规则规定,如果函数模板在实例化时会产生无效的函数声明,就好像这个函数不存在一样,编译器会尝试搜索另一个匹配而不是放弃。
这是在(C)发生的事情,我们尝试调用(A)但如果失败(即无效表达式在template<class U, typename ...>
内产生,我们将最终调用(B)。
(B)与(A)的成功实例化不是很好的匹配,但如果(A)不能被实例化,(B)就会这样做。
答案 1 :(得分:2)
您需要使用特质类和SFINAE,如下所示:
template<class A, typename B>
struct has_inner
{
private:
template<class T, typename U>
static std::true_type has(typename T::template inner<U>*);
template<class, typename>
static std::false_type has(...);
public:
static constexpr auto value = decltype(has<A, B>(nullptr))::value;
};
现在您可以使用它并获得正确的结果:
static_assert(has_inner<ok, float>::value, "ok does not have inner"); // OK
static_assert(has_inner<dummy, float>::value, "dummy does not have inner"); // ERROR