Would concepts possibly replace type traits in C++?

Viewed 687

I have already knew that concept is a compile-time predicate which can constrain templates or auto.

I have discovered that the concepts will return the prvalue of type bool, so I could print it to test myself.

I also read this specific question Can concepts(C++20) be used as a boolean? and the answer which clarifies the concepts that can be used as boolean values.

[temp.names] (8) A concept-id is a simple-template-id where the template-name is a concept-name. A concept-id is a prvalue of type bool, and does not name a template specialization. A concept-id evaluates to true if the concept's normalized constraint-expression is satisfied ([temp.constr.constr]) by the specified template arguments and false otherwise.

For example:

template <typename T>
concept meowable = requires (T obj) {
    { obj.meow() } -> std::same_as<T&>;
};

struct Dog {};
struct Cat { Cat& meow(); };
struct Wolf { Wolf& woof(); };

Application:

std::cout << std::boolalpha;

std::cout << "Has meow?" << '\n'
          << "Dog:  " << meowable<Dog>  << '\n'
          << "Cat:  " << meowable<Cat>  << '\n'
          << "Wolf: " << meowable<Wolf> << '\n';

Output:

Has meow?
Dog: false
Cat: true
Wolf: false

I could also create type trait out of concepts:

template <typename T>
struct has_meow : std::bool_constant<meowable<T>> {};

template <typename T>
constexpr bool has_meow_v = has_meow<T>::value;

Application:

std::cout << "Has meow?" << '\n'
          << "Dog:  " << has_meow_v<Dog> << '\n'
          << "Cat:  " << has_meow_v<Cat> << '\n'
          << "Wolf: " << has_meow_v<Wolf> << '\n';

Which outputs the same as above.

From what I've learned about type traits, they are techniques that give an ability to inspect the "properties" of the type which includes compile-time predicate (not concepts, for example: is_void, is_bounded_array). They are part of "reflection" as of now if I'm not mistaken.


My main question is: Would concepts possibly replace type traits?


It seems like a cycle: concepts use type traits, and type traits may possibly use concepts.

  • Will concepts help type traits to extend in the world of template metaprogramming?
  • When do I apply these concepts or requires expressions as a "helper" / "competent" to type traits when "misused" as the same way std::enable_if did and maybe not used as a constraint. (It's fine for me if it's misused or applied indirectly to the language)

NOTE: I don't ask how is concepts used in a correct way, but I tried to ask if concepts possibly replace type traits in terms of the compile-time predicate.

1 Answers

(Note: this answer was written before OP started updating and expanding the original question)

When do I apply these kinds of stuff in C++?

The fact that a concept-id is a prvalue of type bool is essential when making use of concepts in the constraint-logical-or-expression of a requires-clause:

template<...>
void f() requires .... {}
//                ^^^^ - constraint-logical-or-expression 
//       
//       ^^^^^^^^^^^^^ - requires-clause 

Where, particularly, as per [temp.pre]/1, the grammar for a requires-clause is:

requires-clause:
  requires constraint-logical-or-expression

where constraint-logical-or-expression, in turn, is a primary-expression, which includes requires-expression:s.

E.g. as in the following (contrived) example:

#include <iostream>
#include <type_traits>

template <typename T>
concept has_static_meow = requires (T obj) {
    { decltype(obj)::meow() } -> std::same_as<void>;
};

template <typename T>
concept has_static_bark = requires (T obj) {
    { decltype(obj)::bark() } -> std::same_as<void>;
};

struct Dog { static void bark() { std::cout << "bark\n"; }};
struct Cat { static void meow() { std::cout << "meow\n"; } };

template<typename T>
struct MakeNoise {
    static void bark() requires has_static_bark<T> { T::bark(); }
    static void meow() requires has_static_meow<T> { T::meow(); }
};

int main() {
    MakeNoise<Dog>::bark();
    // MakeNoise<Dog>::meow(); // error with a nice error message
    MakeNoise<Cat>::meow();
    // MakeNoise<Cat>::bark(); // error with a nice error message
}
Related