I am learning about C 20 concepts and I came across the following example that I don't know if it is well-formed(according to the standard) or not. The program is accepted by all compilers: Demo.
Basically, I want to know if this is the correct usage of concepts.
//define Element so that you can use it in vector require clause
template<typename T> bool constexpr Element(){
if constexpr(std::is_same_v<T, int>)
{
return true;
}
else
{
return false;
}
};
template<typename T> // for all types T
requires (Element<T>()) // such that T is an Element
class vector {
//...
};
int main()
{
vector<int> v;
}
My question is that is this well-formed or is it ill-formed NDR. If there is anything wrong then how can we correct it here.
CodePudding user response:
There is no "C concept" in this code, as evidenced by the lack of the use of the concept
keyword. You are using a template definition constrained by a requires
clause, but that requires
clause doesn't use an actual concept
.
As for the validity of the code, it is legal C . Though it's also really bad C . Your use of if constexpr
over-complicates what could just be return std::is_same_v<T, int>;
. Which means that your Element
function could be an Element
concept rather than a function call: template<typename T> concept Element = std::is_same_v<T, int>;
. And since C 20 has the std::same_as
concept, you don't even need that. You should always use a standard library concept if one will fit.
CodePudding user response:
Yes, but no! It is well-formed because you are using type traits, which are compile-time boolean values, which are interchangeable with actual concepts and can be part of concept definitions.
At the same time, real concepts support a richer definition and usage syntax than booleans. For example, they can appear in template and function argument lists. Something like the following, although still a bit contrived, would make more sense to me:
#include <concepts>
template<std::integral T>
T constexpr foo_it(T value)
{
if constexpr(std::same_as<T, int>) {
// Do something specific for int values
} else {
// Generic function body for other integers
}
return value;
}
template<std::integral T>
class vector {
//...
};
int main()
{
vector<int> v; // OK
vector<double> w; // error: double is not integral
}
Your vector class example is odd in the sense that, if you were to design a vector only for integers, it would not need to be a template.
Apart from the usual learning materials, I've found it quite helpful to listen to Bjarne Stroustrup himself on this topic. Here is a short excerpt where he explains it to an interviewer.