检查嵌套的模板化类

Checking for a nested, templated class

本文关键字:嵌套 检查      更新时间:2023-10-16

我正在尝试构建一个检查嵌套模板类是否存在的trait。这是我检查类O是否具有模板参数T的嵌套类inner的方法:

template <typename O, typename T> struct has_inner {
    static const bool value = std::is_class<typename O::template inner<T> >::value;
};

但是,这不能正常工作。给定两个示例类dummyok

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之前实际形成模板表达式。因此,我看到了两个解决方案:

  1. 告诉编译器延迟模板展开,或者
  2. 使用完全不同的方法。

但是,我也不知道怎么表演,有人能帮我吗?

问题

您通常使用并依赖于SFINAE来实现这样的特性,而您的实现并没有利用这些特性。

如前所述,编译器将尝试实例化typename O::template inner<T>,无论是否可能;如果不可能,编译器会在你面前抛出一个错误诊断。

你需要做的是一个条件检查,看看T是否真的有一个模板类,如果没有,就不实例化它。


解决方案- SFINAE救援!

实现可能像下面的代码片段一样,后面会有解释。

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_typestd::false_type,它们都是处理类型特征的结果时的标准行为。


SFINAE规则规定,如果函数模板在实例化时产生无效的函数声明,就好像这个函数不存在一样,编译器将尝试搜索另一个匹配的函数,而不是放弃。

这是在(C)发生的事情,我们尝试调用(A),但如果失败(即。如果在template<class U, typename ...>中出现无效表达式yield,我们将最终调用(B)。

(B)不如(a)的成功实例化匹配,但是如果(a)不能实例化…

你需要使用一个trait类和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