Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use C++ concepts with type_traits?

I want to abstract type interfaces and I am having a hard time to decide between the two C++ means of concepts and type_traits. Consider the follow scenario:

template <typename T>
int foo(const T& t) { return t.size() + t.size(); }

The type T is requested to provide int T::size() const. For creating better error messages, increasing usability for user-defined types of the client and giving him a well-defined list of what an acceptable type must be capable of, a concept can be introduced:

template <typename T>
concept SizeHaving = requires(T t) {
    t.size();
}

template <SizeHaving T>
int foo(const T& t) { return t.size() + t.size(); }

But what if a client type in principle could fulfill the concept, but technically doesn't (and cannot be changed, as it may be part of a third-party library)? A type_trait could help out:

struct Special {
   int Dimension() const; // logically identical to size(), but a different name
};

template <typename T>
struct Type_traits {
    static int size(const T& t) { return t.size(); }
};

template <> // user can provide the specialization for the custom type
struct Type_traits<Special> {
    static int size(const Special& c) { return c.Dimension(); }
};

template <typename T>
int foo(const T& t) {
    return Type_traits<T>::size(t) + Type_traits<T>::size(t);
}

If the standard implementation of Type_trait does not suit for a custom type (and no specialization is provided), there will be again rather unfortunate error messages. How can I get concepts into the play? What I've tried is

template <typename T>
concept SizeHaving = requires(T t) {
    Type_traits<T>::size(t);
}

but the expression constraint will be technically satisfied for any type, whether Type_traits<T>::size() can be explicitly instantiated or not, rendering this concept useless. What can I do?

like image 894
Jodocus Avatar asked Oct 28 '25 03:10

Jodocus


1 Answers

You might also constraint your traits:

template <typename T>
concept SizeHaving = requires(const T& t) {
    t.size(t);
};

template <typename T>
struct Type_traits
{
    auto size(const T& t) requires(SizeHaving<T>) { return t.size(); }
};

template <typename T>
concept TraitSizeHaving = requires(T t) {
    Type_traits<T>::size(t);
};

template <TraitSizeHaving T>
int foo(const T& t) {
    return Type_traits<T>::size(t) + Type_traits<T>::size(t);
}

And then specialize for your custom type:

struct Special {
   int Dimension() const; // logically identical to size(), but a different name
};

template <> // user can provide the specialization for the custom type
struct Type_traits<Special> {
    static int size(const Special& c) { return c.Dimension(); }
};

Demo.

Note: requires(SizeHaving<T>) should be done on member, and not the class to allow specialization of the class.

like image 180
Jarod42 Avatar answered Oct 30 '25 16:10

Jarod42