3

Many of Boost's SFINAE helpers have appeared in the std library with C++11, but has_dereference doesn't seem to have. Other than this feature, I've managed to eliminate a Boost dependency from my package, and I'd like to get rid of it entirely, so how best to get the same effect using just C++11 std features?

andybuckley
  • 1,114
  • 2
  • 11
  • 24
  • 2
    Can't you just look at the implementation? – Nicol Bolas Apr 12 '16 at 21:03
  • I don't know the license exactly, but you can probably just copy paste the Boost implementation into you code. Should be header only and not too long. – Baum mit Augen Apr 12 '16 at 21:09
  • @Nicol I guess you didn't? ;-) It's done via covert (and complex-looking) #defines like BOOST_TT_FORBIDDEN_IF, which are used by an internal has_prefix_operator.hpp header and then undefined again. So not easy to reverse-engineer and definitely not just a matter of copying the code from Boost into std-based code. – andybuckley Apr 12 '16 at 21:11
  • 1
    Well no, I really didn't and apparently I guessed wrong. :) Does [this](https://stackoverflow.com/questions/6534041/how-to-check-whether-operator-exists) help? – Baum mit Augen Apr 12 '16 at 21:14
  • @BaummitAugen Ah, that looks useful. I'll have a try with that tomorrow, thanks! – andybuckley Apr 12 '16 at 21:36

3 Answers3

6

The easiest way to check if a class has some function with no external dependencies is generally with the void_t idiom.

// Define this once in your project somewhere accessible
template <class ... T>
using void_t = void;

The trick then is always the same; you define a class template that inherits from std::false_type:

template <class T, class = void>
struct has_dereference : std::false_type {};

This is the "fallback" class template. Now we're going to define a specialization that only works when the type has the property we want:

template <class T>
struct has_dereference<T, void_t<decltype(*std::declval<T>())>> : std::true_type {};

To use, just do:

bool x = has_dereference<int*>::value;
bool y = has_dereference<int>::value;

etc.

I will add that technically, operator* is actually a family of functions; the operator can be both CV qualified and value category qualified. Whenever you perform detection on a type, you are actually doing detection within this family. I won't get more into details because it's rarely encountered in practice (operator* is rarely value category qualified, and the operator almost always has a const version, and volatile rarely comes up) but it's worth being aware of in case you see something surprising.

This technique is worth knowing about, especially if you are doing meta-programming without dependencies like Boost or Hana. You can read more about void_t here: How does `void_t` work.

Community
  • 1
  • 1
Nir Friedman
  • 17,108
  • 2
  • 44
  • 72
4

This is a neat little SFINAE trait writing helper. It uses std::void_t, which you can reimplement if you lack it.

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;

Once you have that, your problem is easy.

template<class T>
using deref_result = decltype(*std::declval<T>());

template<class T>
using can_deref = can_apply<deref_result, T>;

The idea here is to hide the std::void_t machinery. You write a trait that expresses "result of some computation", and from that we can get "is that computation valid".

A highly portable void_t looks like:

namespace details {
  template<class...>struct voider{using type=void;};
}
template<class...Ts>
using void_t=typename voider<Ts...>::type;

doing it in one line breaks some older compilers, and the 2 line version is easy enough, might as well.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
0

A modified version of Yakk's:

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

O'Neil
  • 3,790
  • 4
  • 16
  • 30