我想使用 SFINAE(使用 void_t)来确定类模板特化或实例化是否定义了某个成员类型。但是主类模板有一个 static_assert
在它的 body 里。
是否可以在不修改主模板且不使用 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 的 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 ,但我对那里接受的答案不满意。因为在这里我们不能“注入(inject)‘补丁’来匹配 static_assert”,因为一旦使用任何类型参数实例化主模板,断言将始终失败。
编辑:上面我描述了我为前面明确说明的抽象问题提供上下文的具体动机。这个具体动机是指 gcc 4.7,但请尽量独立于评论和答案中的 libstdc++ 4.7 实现细节。这只是一个例子。可以有任何类型的 C++ 库,它们可能具有类似于 X
定义的主类模板。 .
最佳答案
我真的不这么认为。 static_assert
在类级别上下文旨在确保您永远不会使用它无法处理的类型来实例化类。这样做是为了避免当您使用未实现预期概念的类型实例化一个类时产生的遥远问题,但直到您进行调用使用预期缺失的东西时才发现......或者更糟的是不要' 然后它在维护期间出现。
所以 static_assert
意味着一旦您尝试实例化该类就会爆炸,正如您正在尝试做的那样。
不过,您可能会考虑一个选项:可用于避免该问题的外部类。基本上是一个特征类,它可能默认为一些简单的实现,但被 X<T>
覆盖.虽然这可能非常脆弱,所以我不知道我会在没有认真考虑其他选项(例如更改问题类别)的情况下将它引入具有很长生命周期的东西。
关于c++ - 使用 SFINAE 时如何避免触发 static_assert?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34425171/