相当于 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...>>;

DEMO