The situation is exactly analogous to noexcept(noexcept(...))
. Sure, this sounds more like a bad thing than a good thing, but let me explain. :) We'll start with what you already know:
C++11 has "noexcept
-clauses" and "noexcept
-expressions." They do different things.
A noexcept
-clause says, "This function should be noexcept when... (some condition)." It goes on a function declaration, takes a boolean parameter, and causes a behavioral change in the declared function.
A noexcept
-expression says, "Compiler, please tell me whether (some expression) is noexcept." It is itself a boolean expression. It has no "side effects" on the behavior of the program — it's just asking the compiler for the answer to a yes/no question. "Is this expression noexcept?"
We can nest a noexcept
-expression inside a noexcept
-clause, but we typically consider it bad style to do so.
template<class T>
void incr(T t) noexcept(noexcept(++t)); // NOT SO HOT
It's considered better style to encapsulate the noexcept
-expression in a type-trait.
template<class T> inline constexpr bool is_nothrow_incrable_v =
noexcept(++std::declval<T&>()); // BETTER, PART 1
template<class T>
void incr(T t) noexcept(is_nothrow_incrable_v<T>); // BETTER, PART 2
C++20 has "requires
-clauses" and "requires
-expressions." They do different things.
A requires
-clause says, "This function should participate in overload resolution when... (some condition)." It goes on a function declaration, takes a boolean parameter, and causes a behavioral change in the declared function.
A requires
-expression says, "Compiler, please tell me whether (some set of expressions) is well-formed." It is itself a boolean expression. It has no "side effects" on the behavior of the program — it's just asking the compiler for the answer to a yes/no question. "Is this expression well-formed?"
We can nest a requires
-expression inside a requires
-clause, but we typically consider it bad style to do so.
template<class T>
void incr(T t) requires (requires (T t) { ++t; }); // NOT SO HOT
It's considered better style to encapsulate the requires
-expression in a type-trait...
template<class T> inline constexpr bool is_incrable_v =
requires (T t) { ++t; }; // BETTER, PART 1
template<class T>
void incr(T t) requires is_incrable_v<T>; // BETTER, PART 2
...or in a C++20 concept.
template<class T> concept Incrable =
requires (T t) { ++t; }; // BETTER, PART 1
template<class T>
void incr(T t) requires Incrable<T>; // BETTER, PART 2
requires
might be used for method of template class, and it would be "ambiguous"template <typename T> struct S {void f(T t) requires requires (T x) {x + x;} { t + t;} };
– Nonmaterialnoexcept(noexcept(...))
. – Idiosyncrasynoexcept
there is ambiguity.noexcept(f())
might mean to benoexcept
iff()
evaluates to true or iff()
isnoexcept
. – Bonniebonnsrequires
clause can have any constexpr predicate, no? – Privetrequires
it knows the next symbol is the requirement, so we need the additionalrequires
to tell it it needs to parse that requirement as an ad-hoc requirement. – Assairequires
are homonyms in my opinion: they look the same, spell the same, smell the same, but are intrinsically different. If I were to suggest a fix, I'd suggest to rename one of them. – Andrasadd<T>()
requires aT
that in turn requires the ability to be added to otherT
s. So, while it looks repetitive, it makes sense if you think about it enough. (Where "I must be addable" is the expression, and "the function needs something that must be addable" is the constraint.) – Hexaemeron