.
// Example 4: Why Can't We Use Functions in "requires"?
// This shows the difference between concepts and constexpr functions
#include <iostream>
#include <concepts>
// ==========================================
// Define types
// ==========================================
struct Dog {
const char* name = "MareArts.com";
};
struct Cat {
const char* name = "Whiskers";
};
// ==========================================
// METHOD 1: Using CONCEPT (works in requires)
// ==========================================
template <typename T>
concept HasName = requires(T t) {
{ t.name } -> std::convertible_to<const char*>;
};
template <typename T>
requires HasName<T> // ✅ Works! Concept in requires clause
void printNameWithConcept(T animal) {
std::cout << "Name: " << animal.name << "\n";
}
// ==========================================
// METHOD 2: Using CONSTEXPR FUNCTION (doesn't work in requires!)
// ==========================================
template <typename T>
consteval bool hasNameFunction() {
return requires(T t) {
{ t.name } -> std::convertible_to<const char*>;
};
}
// ❌ This won't compile!
// template <typename T>
// requires hasNameFunction<T>() // ❌ ERROR: Can't use function in requires!
// void printNameWithFunction(T animal) {
// std::cout << "Name: " << animal.name << "\n";
// }
// ==========================================
// METHOD 3: Using CONSTEXPR FUNCTION with "if constexpr" (works!)
// ==========================================
template <typename T>
void printNameIfConstexpr(T animal) {
// ✅ Works! Function in "if constexpr"
if constexpr (hasNameFunction<T>()) {
std::cout << "Name: " << animal.name << "\n";
}
else {
std::cout << "No name!\n";
}
}
// ==========================================
// WHY THE DIFFERENCE?
// ==========================================
/*
"requires" clause:
- Part of template declaration
- Needs a CONCEPT (compile-time constraint)
- Determines if template can even exist
- Syntax: template <...> requires CONCEPT<T>
"if constexpr":
- Inside function body
- Can use ANY compile-time expression
- Chooses which code branch to keep
- Syntax: if constexpr (EXPRESSION)
*/
// ==========================================
// Main
// ==========================================
int main() {
Dog dog;
Cat cat;
std::cout << "=== Using Concept in 'requires' ===\n";
printNameWithConcept(dog);
printNameWithConcept(cat);
std::cout << "\n=== Using Function in 'if constexpr' ===\n";
printNameIfConstexpr(dog);
printNameIfConstexpr(cat);
return 0;
}
/*
SUMMARY - Where Can You Use What?
┌──────────────────────────┬──────────────┬──────────────────┐
│ │ "requires" │ "if constexpr" │
├──────────────────────────┼──────────────┼──────────────────┤
│ Concept │ ✅ │ ✅ │
│ Constexpr Function │ ❌ │ ✅ │
│ Any compile-time expr │ ❌ │ ✅ │
└──────────────────────────┴──────────────┴──────────────────┘
This is why:
- Factories use concepts in "requires" (IsReferenceAlgorithm concept)
- Dispatcher uses constexpr functions in "if constexpr" (IsReferenceAlgorithm())
After refactoring:
- We removed the concept (not needed anymore)
- Kept the constexpr function (used in dispatcher)
- Removed algorithm checks from factory "requires" clauses
*/
..
.
### 4. Why Not Both? (04_why_not_both.cpp)
**What it teaches:** Why we can't mix them up
```
┌──────────────────────────┬──────────────┬──────────────────┐
│ │ "requires" │ "if constexpr" │
├──────────────────────────┼──────────────┼──────────────────┤
│ Concept │ ✅ │ ✅ │
│ Constexpr Function │ ❌ │ ✅ │
└──────────────────────────┴──────────────┴──────────────────┘
```
**Key Point:** Constexpr functions DON'T work in `requires` clauses!
..
Now #1~#4 series is done.
g++ -std=c++20 01_concept_basics.cpp -o 01_concepts
g++ -std=c++20 02_constexpr_functions.cpp -o 02_constexpr
g++ -std=c++20 03_dispatcher_pattern.cpp -o 03_dispatcher
g++ -std=c++20 04_why_not_both.cpp -o 04_why
recap!
## Key Takeaways π―
1. **Concepts** check if types have certain features
- Used in `requires` clauses
- Like: `requires HasName<T>`
2. **Constexpr functions** run at compile time
- Used in `if constexpr` statements
- Like: `if constexpr (isDog<T>())`
3. **Dispatcher pattern** separates concerns:
- **Dispatcher** checks algorithm type (using `if constexpr` + constexpr functions)
- **Factory** checks direction only (using `requires` + concepts)
4. **You can't mix them:**
- Constexpr functions DON'T work in `requires` clauses
- That's why we removed the algorithm check from factory `requires` clauses!
Thank you!
study.marearts.com
No comments:
Post a Comment