I want to constraint the template parameters of a policy class.
That is, when I call Foo<policy>
, I want the compiler to stop here if the policy class does not fulfill the requirements I want.
Complete non-working example
To simplify the problem, let's consider just the requirement that the policy class has to declare a static variable that itself fulfill another concept (here, the Acceleration concepts from the mp-units library.
#include <units/isq/si/si.h>
using units::isq::Acceleration;
// A policy
struct earth
{
// requirement seems to be fulfilled
static inline constexpr Acceleration auto gravity = standard_gravity<>;
};
// Let's define a concept because I will need soon to use a set of more than 1 requirements
template <typename T>
concept SphericBody = requires(T)
{
{ T::gravity } -> Acceleration;
};
// The host class that has a constraint of the template argument
template<SphericBody T>
class Foo
{
// ...
}
int main()
{
Foo<earth> // does not compile :'(
}
It fails with the following compiler message:
‘T::gravity’ does not satisfy return-type-requirement
{ T::gravity } -> units::isq::Acceleration;
In the current version of the mp-units library, the Acceleration concept declaration is the following:
#include <units/concepts.h>
#include <units/isq/dimensions/length.h>
#include <units/isq/dimensions/time.h>
namespace units::isq {
template<typename Child, Unit U, typename...>
struct dim_acceleration;
template<typename Child, Unit U, DimensionOfT<dim_length> L, DimensionOfT<dim_time> T>
struct dim_acceleration<Child, U, L, T> : derived_dimension<Child, U, exponent<L, 1>, exponent<T, -2>> {};
template<typename T>
concept Acceleration = QuantityOfT<T, dim_acceleration>;
} // namespace units::isq
What am I doing wrong?
I am aware of this related question: C++ Concepts - Can I have a constraint requiring a function be present in a class? but it focuses on non-static member variables.
Minimal working example
As requested by @HolyBlackCat, I tried my best to come with a minimal working example. The member variable is now a simple integer. Simply adding the requires clause works:
template <typename T>
concept HasGravity = requires(T t)
{
{ t.gravity } -> std::same_as<int&>;
};
struct myearth
{
int gravity;
};
// The host class that has a constraint of the template argument
template<HasGravity T>
class Foo
{};
// using policy_t = Foo<earth> // compiles
Minimal NON working example
In this case, the requirement is exported to a concept, and it does not compile anymore.
template <typename T>
concept IsAcceleration = std::same_as<int&>;
};
// Let's define a concept because I will need soon to use a set of more than 1 requirements
template <typename T>
concept HasGravity = requires(T t)
{
{ t.gravity } -> IsAcceleration;
};
// A policy
struct myearth
{
int gravity;
};
// The host class that has a constraint of the template argument
template<HasGravity T>
class Foo
{};
// using policy_t = Foo<earth> // does not compile
Error:
note: constraints not satisfied
test.cpp:45:9: required for the satisfaction of ‘HasGravity<T>’ [with T = myearth]
test.cpp:45:22: in requirements with ‘T t’ [with T = myearth]
test.cpp:47:7: note: ‘t.gravity’ does not satisfy return-type-requirement
47 | { t.gravity } -> IsAcceleration;