Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unconstrained requires-expression parameter

Can I use constrained generic types inside C++20 constraints for a concept? As an example, say I'd like to write a requirement that a candidate class T have a function func that can take an argument of any type that satisfies std::ranges::range:

template<typename T>
concept Foo = requires (T t, std::ranges:range s) {
    { t.func(a); } -> std::convertible_to<double>;
}

But GCC gives me an error message that a placeholder type cannot be used where I've put std::ranges::range.

like image 997
Anthony Hall Avatar asked May 26 '26 03:05

Anthony Hall


1 Answers

It seems to me that your looking a struct with an (declared only) operator T () for a generic T

struct generic_argument
 {
   template <typename T>
   operator T () const;
 };

so you can pass it (inside a decltype()) for every expected argument and also a template argument.

So your concept (sorry... I simplify it removing the ranges argument) become (if I understand what do you want) something as

template<typename T>
concept Foo 
   = std::convertible_to<decltype(std::declval<T>().func(std::declval<generic_argument>())),
                         double>;

The following is a full compiling example

#include <iostream>

struct A { double func (int a) { return a; } };
struct B { double not_func (long a) { return a; } };
struct C { std::string func (char) { return "abc"; } };
struct D { float func (auto) { return 1.0f; } };

struct E { double func (int a) { return a; }
           std::string func (char) { return "abc"; } };

struct generic_argument
 {
   template <typename T>
   operator T () const;
 };

template<typename T>
concept Foo 
   = std::convertible_to<decltype(std::declval<T>().func(std::declval<generic_argument>())),
                         double>;

template <Foo T>
void bar (T const &)
 { std::cout << "bar, Foo version\n"; }

template <typename T>
void bar (T const &)
 { std::cout << "bar, generic version\n"; }

int main()
 {
   bar(A{}); // print bar, Foo version
   bar(B{}); // print bar, generic version [no func() function]
   bar(C{}); // print bar, generic version [no convertible to double]
   bar(D{}); // print bar, Foo version
   bar(E{}); // print bar, generic version [two func() function]
 }

I'm in doubt regarding the E case: two func() methods, one only return a type convertible to double, from bar(E{}) we get "bar, generic version".

like image 111
max66 Avatar answered May 27 '26 16:05

max66