"Conditional" alias templates
Asked Answered
A

2

8

In a type like the unspecialized template struct pointer_traits (i.e. template <class Ptr> struct pointer_traits), there exists a member alias template rebind that is defined to be Ptr::rebind<U>, if it exists, or some other type otherwise. Though I have seen a few answers on checking whether a certain member exists, how does one implement a "conditional" alias template like pointer_traits::rebind? That is, as if by the following pseudo-C++:

template <typename T> using type = has_type<T::U> ? int : float;

or

template <typename T> using type = if_has_type<T::U, int, float>::type;

I considered using something like the method depicted at https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Member_Detector (section "Detecting member types"), but I don't know how to implement a helper struct whose [sole] member type depends on the existence of another member type.

Afterburning answered 18/1, 2017 at 16:9 Comment(0)
L
11

By using std::conditional from <type_traits>. It's as simple as:

using type = typename std::conditional<bool, int, float>::type;

or

using type = std::conditional_t<bool, int, float>;

where you replace bool with some condition, evaluable at compile-time to a boolean value. In this case the condition is the check for an existing member.

If the condition is true type becomes an alias to int, otherwise to float.

FULL EXAMPLE (Check if difference_type is a member type.)

namespace detail {

template<class Ptr>
using ptrait_diff = typename Ptr::difference_type;

template<class Ptr, bool = is_detected<ptrait_diff, Ptr>::value>
struct ptrait_diff_t { 
    using type = ptrdiff_t;
};

template<class Ptr>
struct ptrait_diff_t<Ptr, true> {
    using type = typename Ptr::difference_type;
};

} // namespace detail

and then:

template<class Ptr>
struct pointer_traits
{
    using difference_type = typename detail::ptrait_diff_t<Ptr>::type;
};

Implementation of is_detected can be found HERE.

Lloyd answered 18/1, 2017 at 16:17 Comment(5)
I think op is more interested in how the condition would look rather than std::conditionalAnapest
The implementation of std::conditional seems easy to understand, but how does is_detected work? Also, though I'm a fan of the new C++17 features, pointer_traits was implemented in C++11. Without using [experimental] C++17 features, how would one implement this?Afterburning
@AliciaRose Without using [experimental] C++17 features, how would one implement this? Ugly and painfully. You can look up the source code for your standard library online.Lloyd
"std::is_detected is present in C++17" Nope. "using difference_type = conditional_t<is_detected<detail::ptrait_diff, Ptr>::value, typename Ptr::difference_type, ptrdiff_t>;" Double nope. You can't attempt to access typename Ptr::difference_type until you know that it exists. And if you are trying to use the detection idiom, this is the textbook use case for detected_or, not is_detected.Shelf
@Shelf Right. Thanks for noticing. I didn't test-run any of this. For some reason I was of the belief that std::is_detected had made it into the standard. You get disappointed every day...Lloyd
D
5

This is the problem std::conditional is designed to solve.

#include <type_traits>
template<bool condition> 
using type = std::conditional_t<condition, int, float>;

static_assert(std::is_same<type<true>,  int>::value,   "type<true> should be int");
static_assert(std::is_same<type<false>, float>::value, "type<false> should be float");
Disassemble answered 18/1, 2017 at 16:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.