Would concepts possibly replace type traits in C++? [closed]
Asked Answered
D

1

12

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.

Durian answered 2/6, 2021 at 11:59 Comment(2)
I'm happy you're asking this question. It shows that you are getting deeper into C++ and thinking critically. Concepts are used in meta-programming (templates) to select the appropriate implementation of a class or function (or not select one). That said, it's a really broad question, and an adequate discussion may be out of scope.Hyatt
So, you used this "stuffs" in your question. And it asks how you can use it. So, asked and answered?Late
D
0

(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
}
Drugget answered 2/6, 2021 at 12:43 Comment(3)
... but you still need to literally answer OP's questions :-(Andersen
@Andersen When I started on this answer I just read OP's version 1 (or possibly 2) of his question, where the main question (before the question exploded and changed) was "When do I apply these kinds of stuff in C++?".Drugget
I apologize if I asked in an inconsistent way but I tried to change my post every time on how I mean it.Durian

© 2022 - 2024 — McMap. All rights reserved.