I've been playing around with concepts. Here's a minimal example where I'm trying to create a concept based on method signatures:
template<typename T>
concept bool myConcept() {
return requires(T a, int i) {
{ a.foo() } -> int;
{ a.bar(i) } -> int;
};
}
struct Object {
int foo() {return 0;}
int bar(int) {return 0;}
};
static_assert(myConcept<Object>(), "Object does not adhere to myConcept");
To my surprise writing { a.bar(int) } -> int
did not work, so I resorted to adding an additional argument to the requires
expression. This seems a bit strange and I was wondering if there is a way to do the same thing. Another thing that worked was using something like { a.bar((int)0) } -> int
, but I find this worse.
The signature of a method consists of the name of the method and the description (i.e., type, number, and position) of its parameters.
Method Signature According to Oracle, the method signature is comprised of the name and parameter types. Therefore, all the other elements of the method's declaration, such as modifiers, return type, parameter names, exception list, and body are not part of the signature.
Function Signatures. A function signature consists of the function prototype. What it tells you is the general information about a function, its name, parameters, what scope it is in, and other miscellaneous information. C++ "mangle"s function names so that they are pretty, though in all truth they can be very ugly.
In computer programming, especially object-oriented programming, a method is commonly identified by its unique method signature, which usually includes the method name, and the number, types and order of its parameters. A method signature is the smallest type of a method.
Concepts check expressions, and a.bar(int)
is not one. By writing
{ a.foo(int) } -> int
you ask the compiler to check that the aforementioned expression has type int
. Which doesn't make sense.
You did find a valid alternative; another one might be, since the type of a.bar(x)
doesn't depend on x
' value:
template<typename T>
concept bool myConcept() {
return requires(T a) {
{ a.foo() } -> int;
{ a.bar(0) } -> int;
};
}
struct Object {
int foo() {return 0;}
int bar(int) {return 0;}
};
static_assert(myConcept<Object>(), "Object does not adhere to myConcept");
As actual value of the type doesn't matter, I'd suggest using int{}
as argument. This documents the purpose of the argument somewhat better, IMO:
{ a.bar(int{}) } -> int;
Obviously this won't work with types for which there is no default constructor. In templates, one would use std::declval
to work around similar issue, but here GCC errors:
error: static assertion failed: declval() must not be used!
But there is nothing to stop us from writing equivalent (but unimplemented) function to be used with concepts, like this:
#include <type_traits>
template <class T>
typename std::add_rvalue_reference<T>::type makeval();
template<typename T>
concept bool myConcept() {
return requires(T a, int i) {
{ a.foo() } -> int;
{ a.bar(makeval<int>()) } -> int;
};
}
struct Object {
int foo() {return 0;}
int bar(int) {return 0;}
};
static_assert(myConcept<Object>(), "Object does not adhere to myConcept");
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With