使用SFINAE时如何避免触发static_assert?

时间:2015-12-22 22:02:56

标签: c++ templates c++11 sfinae static-assert

我想使用SFINAE(带void_t)来确定类模板特化或实例化是否定义了某个成员类型。但是,主类模板中有一个static_assert。 是否可以在不修改主模板且没有preprocessor tricks

的情况下检索此信息
#include <type_traits>

template <class T>
struct X {
    static_assert(sizeof(T) < 0, "");
};

template <>
struct X<int> {
    using type = int;
};

template <class...>
using void_t = void;

template <class, class = void_t<>>
struct Has : std::false_type {};

template <class T>
struct Has<T, void_t<typename T::type>> : std::true_type {};

int main() {
    static_assert(Has<X<int>>::value == true, "");

    // How to make this compile?
    static_assert(Has<X<char>>::value == false, ""); // ERROR
}

X<int>是一个显式特化,但X<char>是主模板的隐式实例化。当编译器创建此实例化时,整个SFINAE机制将被主模板体内的static_assert声明引起的错误停止。

我的具体动机是:我正在创建一个通用的包装类模板,如果它的模板类型参数具有std::hash<>的特化,我想为它定义一个哈希函数。但是,gcc 4.7会将static_assert放在std::hash<>主模板的定义中。 gcc 4.8和llvm的libc ++的libstdc ++只是简单地声明主模板。因此,我的类模板不适用于gcc / libstdc ++ 4.7。

// GCC 4.7.2
  /// Primary class template hash.
  template<typename _Tp>
    struct hash : public __hash_base<size_t, _Tp>
    {
      static_assert(sizeof(_Tp) < 0,
            "std::hash is not specialized for this type");
      size_t operator()(const _Tp&) const noexcept;
    };

// GCC 4.8.2
  /// Primary class template hash.
  template<typename _Tp>
    struct hash;

这个问题与this one类似,但我对那里接受的答案不满意。因为在这里我们不能注射补丁&#39;要匹配static_assert&#34;因为一旦使用任何类型参数实例化主模板,断言将始终失败。

编辑:上面我描述了我为抽象问题提供背景的具体动机,该问题已在前面明确说明。这个具体的动机是指gcc 4.7,但请尝试独立于评论和答案中的libstdc ++ 4.7实现细节。这只是一个例子。可以有任何类型的C ++库,它们可能具有与X类似定义的主类模板。

1 个答案:

答案 0 :(得分:2)

我真的不这么认为。类级别上下文中的static_assert旨在确保您永远不会实例化具有无法处理的类型的类。它是为了避免当你实例化一个类型没有实现预期概念的类时产生的远程问题,但是在你使用预期缺少的东西进行调用之前你没有发现...或者更糟的是然后它会在维护期间出现。

因此,当您尝试实例化该类时,static_assert意味着爆炸。

您可以考虑一个选项:可以用来避免问题的外部类。基本上是一个traits类,可能默认为一些简单的实现,但被X<T>覆盖。这可能会非常脆弱,所以我不知道我会把它介绍给生命周期长的东西而不认真考虑其他选项,比如更改问题类。