【发布时间】: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<int> 是显式特化,尽管X<char> 是主模板的隐式实例化。当编译器创建这个实例化时,整个 SFINAE 机器会因主模板主体内的 static_assert 声明导致的错误而停止。
我的具体动机是:我正在创建一个通用包装类模板,如果它的模板类型参数具有std::hash<> 的特化,我想为其定义一个哈希函数。然而 gcc 4.7 在std::hash<> 的主模板的定义 中放置了一个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