【问题标题】:How to avoid the triggering of a static_assert when SFINAE is used?使用 SFINAE 时如何避免触发 static_assert?
【发布时间】:2015-12-22 22:02:56
【问题描述】:

我想使用 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&lt;int&gt; 是显式特化,尽管X&lt;char&gt; 是主模板的隐式实例化。当编译器创建这个实例化时,整个 SFINAE 机器会因主模板主体内的 static_assert 声明导致的错误而停止。


我的具体动机是:我正在创建一个通用包装类模板,如果它的模板类型参数具有std::hash&lt;&gt; 的特化,我想为其定义一个哈希函数。然而 gcc 4.7 在std::hash&lt;&gt; 的主模板的定义 中放置了一个static_assert。 gcc 4.8 的 libstdc++ 和 llvm 的 libc++ 只是简单地声明主模板。因此,我的类模板不适用于 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,但我对那里接受的答案不满意。因为在这里我们不能“注入'补丁'来匹配 static_assert”,因为一旦使用任何类型参数实例化主模板,断言总是会失败。

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

【问题讨论】:

  • gcc 4.7 中的主要模板定义是格式错误的 NDR,因此在标准 C++ 中无法解决它,因为任何具有该模板定义的程序都已经格式错误。见open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#1483
  • 4.7 在这种情况下只是被破坏了,不确定您是否可以做些什么。升级到 4.8。或 5.2。
  • 在 c++11 标准库的其他领域,gnu 编译器套件直到最近才起作用。正则表达式直到 4.9 才起作用,本地化直到 5.1。最佳建议:更新你的编译器。
  • @Barry @David Hammen 请尽量独立于 gcc/libstdc++ 4.7 实现细节。这只是为上面明确说明的抽象问题提供上下文。可以有任何其他 C++ 库,它具有类似于X 定义的此类模板
  • 你不能。这样的设计从根本上不是 SFINAE 友好的。比较LWG 2543

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


【解决方案1】:

我真的不这么认为。 static_assert 在类级别上下文中是为了确保您永远不会使用它无法处理的类型实例化一个类。这样做是为了避免当您使用未实现预期概念的类型实例化一个类时产生的遥远问题,但直到您拨打使用预期缺少的东西的调用时才发现......或者更糟的是不要't 然后在维护期间出现。

所以static_assert 意味着在您尝试实例化类时立即爆炸,就像您尝试做的那样。

您可能会考虑一个选项:可以使用外部类来避免该问题。基本上是一个特征类,它可能默认为一些简单的实现,但被X&lt;T&gt; 覆盖。不过这可能非常脆弱,所以我不知道我是否会将它引入寿命较长的东西而不认真考虑其他选项,例如更改问题类。

【讨论】:

    猜你喜欢
    • 2012-08-12
    • 2021-04-24
    • 2015-05-27
    • 1970-01-01
    • 2022-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多