相当于 Boost has_dereference 的 C++11 std
C++11 std equivalent of Boost has_dereference
许多 Boost 的 SFINAE 助手已经出现在 C++11 的 std 库中,但 has_dereference
似乎没有。除了这个特性,我已经设法从我的包中消除了对 Boost 的依赖,我想完全摆脱它,那么如何最好地只使用 C++11 std 特性来获得相同的效果?
检查 class 是否具有某些没有外部依赖性的函数的最简单方法通常是使用 void_t
惯用语。
// Define this once in your project somewhere accessible
template <class ... T>
using void_t = void;
技巧总是一样的;您定义了一个继承自 std::false_type
:
的 class 模板
template <class T, class = void>
struct has_dereference : std::false_type {};
这是 "fallback" class 模板。现在我们要定义一个专门化,只有当类型具有我们想要的 属性 时才有效:
template <class T>
struct has_dereference<T, void_t<decltype(*std::declval<T>())>> : std::true_type {};
要使用,只需执行:
bool x = has_dereference<int*>::value;
bool y = has_dereference<int>::value;
等等
我要补充一点,从技术上讲,operator*
实际上是一个函数族;运算符既可以是 CV 合格的,也可以是价值类别合格的。每当你对一个类型进行检测时,你实际上是在这个家族中进行检测。我不会详细介绍,因为它在实践中很少遇到(operator*
很少是值类别限定的,并且运算符几乎总是有一个 const 版本,而 volatile 很少出现)但值得注意以防万一你看到了令人惊讶的东西。
这项技术值得了解,尤其是当您在不依赖 Boost 或 Hana 的情况下进行元编程时。您可以在此处阅读有关 void_t 的更多信息:How does `void_t` work.
这是一个简洁的 SFINAE 特质编写小助手。它使用std::void_t
,如果你没有它,你可以重新实现它。
namespace details {
template<template<class...>class Z, class v, class...Ts>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply=typename details::can_apply<Z, void, Ts...>::type;
一旦你有了它,你的问题就很简单了。
template<class T>
using deref_result = decltype(*std::declval<T>());
template<class T>
using can_deref = can_apply<deref_result, T>;
这里的想法是隐藏 std::void_t
机器。你写了一个表示 "result of some computation" 的特征,从中我们可以得到 "is that computation valid".
高度便携的 void_t
看起来像:
namespace details {
template<class...>struct voider{using type=void;};
}
template<class...Ts>
using void_t=typename voider<Ts...>::type;
在一行中完成它会破坏一些旧的编译器,而 2 行版本足够简单,也可以。
Yakk 的修改版本:
template <class...> struct pack {};
namespace detail {
template<template <class...> class Z, class Pack, class = void>
struct can_apply_impl : std::false_type {};
template<template<class...>class Z, class...Ts>
struct can_apply_impl<Z, pack<Ts...>, std::void_t<Z<Ts...>> > : std::true_type {};
}
template<template<class...>class Z, class...Ts>
using can_apply = detail::can_apply_impl<Z, pack<Ts...>>;
许多 Boost 的 SFINAE 助手已经出现在 C++11 的 std 库中,但 has_dereference
似乎没有。除了这个特性,我已经设法从我的包中消除了对 Boost 的依赖,我想完全摆脱它,那么如何最好地只使用 C++11 std 特性来获得相同的效果?
检查 class 是否具有某些没有外部依赖性的函数的最简单方法通常是使用 void_t
惯用语。
// Define this once in your project somewhere accessible
template <class ... T>
using void_t = void;
技巧总是一样的;您定义了一个继承自 std::false_type
:
template <class T, class = void>
struct has_dereference : std::false_type {};
这是 "fallback" class 模板。现在我们要定义一个专门化,只有当类型具有我们想要的 属性 时才有效:
template <class T>
struct has_dereference<T, void_t<decltype(*std::declval<T>())>> : std::true_type {};
要使用,只需执行:
bool x = has_dereference<int*>::value;
bool y = has_dereference<int>::value;
等等
我要补充一点,从技术上讲,operator*
实际上是一个函数族;运算符既可以是 CV 合格的,也可以是价值类别合格的。每当你对一个类型进行检测时,你实际上是在这个家族中进行检测。我不会详细介绍,因为它在实践中很少遇到(operator*
很少是值类别限定的,并且运算符几乎总是有一个 const 版本,而 volatile 很少出现)但值得注意以防万一你看到了令人惊讶的东西。
这项技术值得了解,尤其是当您在不依赖 Boost 或 Hana 的情况下进行元编程时。您可以在此处阅读有关 void_t 的更多信息:How does `void_t` work.
这是一个简洁的 SFINAE 特质编写小助手。它使用std::void_t
,如果你没有它,你可以重新实现它。
namespace details {
template<template<class...>class Z, class v, class...Ts>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply=typename details::can_apply<Z, void, Ts...>::type;
一旦你有了它,你的问题就很简单了。
template<class T>
using deref_result = decltype(*std::declval<T>());
template<class T>
using can_deref = can_apply<deref_result, T>;
这里的想法是隐藏 std::void_t
机器。你写了一个表示 "result of some computation" 的特征,从中我们可以得到 "is that computation valid".
高度便携的 void_t
看起来像:
namespace details {
template<class...>struct voider{using type=void;};
}
template<class...Ts>
using void_t=typename voider<Ts...>::type;
在一行中完成它会破坏一些旧的编译器,而 2 行版本足够简单,也可以。
Yakk 的修改版本:
template <class...> struct pack {};
namespace detail {
template<template <class...> class Z, class Pack, class = void>
struct can_apply_impl : std::false_type {};
template<template<class...>class Z, class...Ts>
struct can_apply_impl<Z, pack<Ts...>, std::void_t<Z<Ts...>> > : std::true_type {};
}
template<template<class...>class Z, class...Ts>
using can_apply = detail::can_apply_impl<Z, pack<Ts...>>;