Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write a generic trait to match T::value_type against other traits? [closed]

Tags:

c++

c++17

I'm trying to create a sort of "wrapper trait" that can detect, for some T and some existing unary trait Concept, that:

  • T is "iterable", and
  • T::value_type satisfies Concept

This is useful because I have other code that may expect various types, including std::vector<various types>, and I'd like to use this wrapper trait to enable_if various functions inside said code.

Here's a sketch:

#include <type_traits>
#include <vector>

template <typename T, typename = void>
struct is_iterable : std::false_type {};

template <typename T>
struct is_iterable<T, std::void_t<decltype(std::declval<T&>().begin() == std::declval<T&>().end())>> : std::true_type {};

template <class T>
constexpr bool is_iterable_v = is_iterable<T>::value;


template <typename T, typename = void>
struct iterable_value_type
{
    using type = std::false_type;
};

template <typename T>
struct iterable_value_type<T, std::void_t<decltype(T::value_type)>>
{
    using type = typename T::value_type;
};

template <class T>
using iterable_value_type_t = typename iterable_value_type<T>::type;


// Transforms a concept "does type T have property P" into the concept
// "is type T1 an iterable with value_type T2 where T2 has property P"
template <typename T, template <typename...> typename BaseConcept>
struct CollectionConcept
{
    static constexpr bool value = is_iterable_v<T> && BaseConcept<iterable_value_type_t<T>>::value;
};

int main()
{
    static_assert(std::is_arithmetic<int>::value);
    static_assert(CollectionConcept<std::vector<int>, std::is_arithmetic>::value);
}

Unfortunately the second static_assert triggers and I can't work out why.

How can I implement this?

like image 710
Lightness Races in Orbit Avatar asked Mar 06 '23 01:03

Lightness Races in Orbit


1 Answers

The culprit is this:

template <typename T>
struct iterable_value_type<T, std::void_t<decltype(T::value_type)>>
{
    using type = typename T::value_type;
};

You're applying decltype to a type, not to an expression.

To fix the trait, remove decltype (and prefix with typename because of name dependency):

template <typename T>
struct iterable_value_type<T, std::void_t<typename T::value_type>>
{
    using type = typename T::value_type;
};

[Live example]

like image 168
Angew is no longer proud of SO Avatar answered Apr 07 '23 06:04

Angew is no longer proud of SO