为什么 `has_construct<Alloc, T, Args...>::value` 在 gcc (false) 和 clang (true) 上给出不同的值?
Why does `has_construct<Alloc, T, Args...>::value` give different values on gcc (false) and clang (true)?
我正在尝试实现allocate_traits
,然后我尝试了很多方法,最终使它与gcc(>=5)兼容,下面是class做的判断关于 allocate_traits::construct
template <typename ...>
using type_helper = void;
template <typename T, typename... Args>
struct has_construct_helper {
template <typename Alloc, typename = type_helper<
decltype(declval<Alloc &>().construct
(declval<T *>(), declval<Args>()...))
>> static constexpr true_type test(Alloc, int);
template <typename Alloc> static constexpr false_type test(Alloc, ...);
};
template <typename Alloc, typename T, typename... Args>
using has_construct =
decltype(has_construct_helper<T, Args...>::test(declval<Alloc>(), 0));
并且我使用不包含 construct
的分配器对其进行了测试,它与 gcc (>= 5) 配合使用效果很好,但 clang (>= 3.7) 给我 true
。此代码有效吗?
有一整个文件有同样的错误,可能更简单:
https://gist.github.com/anonymous/9a6125c4796d4c0227cb
问题出在您的 type_helper
别名模板上。对于如何处理未使用的可变参数模板参数,标准中曾经存在歧义。 Clang 决定不理会它们,因此您的 decltype
表达式从未真正被检查过,因此 SFINAE 不会发生。
简单的解决方法是推迟到另一个特征,以便评估参数:
template <typename...> struct voider { using type = void; };
template <typename...Ts> using type_helper = typename voider<Ts...>::type;
这是您的代码的一个稍微简单的版本,它使用 SFINAE 的检测习惯用法。
template <typename...> struct voider { using type = void; };
template <typename...Ts> using void_t = typename voider<Ts...>::type;
template <typename Alloc, typename T, typename, typename... Args>
struct has_construct_helper : std::false_type{};
template <typename Alloc, typename T, typename... Args>
struct has_construct_helper<Alloc, T,
void_t<decltype(declval<Alloc &>().construct
(declval<T *>(), declval<Args>()...))>,
Args...> : std::true_type
{};
template <typename Alloc, typename T, typename... Args>
using has_construct = typename has_construct_helper<Alloc, T, void, Args...>::type;
template <typename Alloc, typename T, typename... Args>
void doConstruct(std::true_type, Alloc &a, T *p, Args&&... args)
{
a.construct(p, forward<Args>(args)...);
}
template <typename Alloc, typename T, typename... Args>
void doConstruct(std::false_type, Alloc &, T *p, Args&&... args)
{
::new (static_cast<void *>(p)) T(forward<Args>(args)...);
}
template <typename Alloc>
class allocator_traits {
public:
//...
template <typename T, typename... Args> static
void construct(Alloc &a, T *p, Args&&... args)
{
doConstruct(has_construct<Alloc,T,Args...>{}, a, p, forward<Args>(args)...);
}
//...
};
我正在尝试实现allocate_traits
,然后我尝试了很多方法,最终使它与gcc(>=5)兼容,下面是class做的判断关于 allocate_traits::construct
template <typename ...>
using type_helper = void;
template <typename T, typename... Args>
struct has_construct_helper {
template <typename Alloc, typename = type_helper<
decltype(declval<Alloc &>().construct
(declval<T *>(), declval<Args>()...))
>> static constexpr true_type test(Alloc, int);
template <typename Alloc> static constexpr false_type test(Alloc, ...);
};
template <typename Alloc, typename T, typename... Args>
using has_construct =
decltype(has_construct_helper<T, Args...>::test(declval<Alloc>(), 0));
并且我使用不包含 construct
的分配器对其进行了测试,它与 gcc (>= 5) 配合使用效果很好,但 clang (>= 3.7) 给我 true
。此代码有效吗?
有一整个文件有同样的错误,可能更简单: https://gist.github.com/anonymous/9a6125c4796d4c0227cb
问题出在您的 type_helper
别名模板上。对于如何处理未使用的可变参数模板参数,标准中曾经存在歧义。 Clang 决定不理会它们,因此您的 decltype
表达式从未真正被检查过,因此 SFINAE 不会发生。
简单的解决方法是推迟到另一个特征,以便评估参数:
template <typename...> struct voider { using type = void; };
template <typename...Ts> using type_helper = typename voider<Ts...>::type;
这是您的代码的一个稍微简单的版本,它使用 SFINAE 的检测习惯用法。
template <typename...> struct voider { using type = void; };
template <typename...Ts> using void_t = typename voider<Ts...>::type;
template <typename Alloc, typename T, typename, typename... Args>
struct has_construct_helper : std::false_type{};
template <typename Alloc, typename T, typename... Args>
struct has_construct_helper<Alloc, T,
void_t<decltype(declval<Alloc &>().construct
(declval<T *>(), declval<Args>()...))>,
Args...> : std::true_type
{};
template <typename Alloc, typename T, typename... Args>
using has_construct = typename has_construct_helper<Alloc, T, void, Args...>::type;
template <typename Alloc, typename T, typename... Args>
void doConstruct(std::true_type, Alloc &a, T *p, Args&&... args)
{
a.construct(p, forward<Args>(args)...);
}
template <typename Alloc, typename T, typename... Args>
void doConstruct(std::false_type, Alloc &, T *p, Args&&... args)
{
::new (static_cast<void *>(p)) T(forward<Args>(args)...);
}
template <typename Alloc>
class allocator_traits {
public:
//...
template <typename T, typename... Args> static
void construct(Alloc &a, T *p, Args&&... args)
{
doConstruct(has_construct<Alloc,T,Args...>{}, a, p, forward<Args>(args)...);
}
//...
};