C++ Concept That Requires a Member Function with an Outputiterator as Parameter

C++ Concept that requires a member function with an OutputIterator as parameter

This is a common problem of asking the wrong question of a concept, where you're trying to use them like you would a base class interface. With base classes, you're declaring the exact, specific functions that the derived classes are to implement. You want the user to implement exactly the function you say they must.

With concepts, you approach the issue from the other direction: what usage are you trying to create?

At some point in your code, you have some object, some iterator, and a size. And you're going to take that object, call a function by passing it the iterator and the size, and you expect a response back of a certain type. And this process has some meaning.

Then that is your concept. It's a concept that is based on at least 2 parameters: the type of the object and the iterator type. So that's what you should create.

If you have this BulkReadable constraint, then you must have some interface constrained on it. An interface that's going to call read. To call read, that interface must have an iterator.

So here are the options:

  1. The interface is given an iterator type (directly or indirectly) by the user. If that's the case, then you just use that type in the function's BulkReadable constraint. If the iterator type is based on a complex set of operations on parameters, then you'll have to do some computations to compute the iterator type.

  2. The iterator is statically determined. Then just use the known iterator type in the constraint.

The point being that, at the point where you're going to try to call read, you know what the iterator type is. And therefore, you can constrain things using that type. Your concept therefore is not really BulkReadable, but BulkReadableFrom.

In short, you shouldn't want to constrain a type on being able to take any type (or any type within some constraints). Check constraints against the actual types you're going to use them with, preferably at the point where they become relevant.

Concept definition requiring a constrained template member function

What you're looking for is for a way for the compiler to synthesize an archetype of Surface. That is, some private, anonymous type that minimally satisfies the Surface concept. As minimally as possible. Concepts TS doesn't currently allow for a mechanism for automatically synthesizing archetypes, so we're left with doing it manually. It's quite a complicated process, since it's very easy to come up with archetype candidates that have way more functionality that the concept specifies.

In this case, we can come up with something like:

namespace archetypes {
// don't use this in real code!
struct SurfaceModel {
// none of the special members
SurfaceModel() = delete;
SurfaceModel(SurfaceModel const& ) = delete;
SurfaceModel(SurfaceModel&& ) = delete;
~SurfaceModel() = delete;
void operator=(SurfaceModel const& ) = delete;
void operator=(SurfaceModel&& ) = delete;

// here's the actual concept
void move_to(point2f );
void line_to(point2f );
void arc(point2f, float);
// etc.
};

static_assert(Surface<SurfaceModel>());
}

And then:

template <typename T>
concept bool Drawable() {
return requires(const T& t, archetypes::SurfaceModel& surface) {
{ t.draw(surface) } -> void;
};
}

These are valid concepts, that probably work. Note that there's a lot of room for even more refinement on the SurfaceModel archetype. I have a specific function void move_to(point2f ), but the concept just requires that it's callable with an lvalue of type point2f. There's no requirement that move_to() and line_to() both take an argument of type point2f, they could both take complete different things:

struct SurfaceModel {    
// ...
struct X { X(point2f ); };
struct Y { Y(point2f ); };
void move_to(X );
void line_to(Y );
// ...
};

This kind of paranoia makes for a better archetype, and serves to illustrate how complex this problem could be.

class template and member function template with requires

The rules for defining template members of class templates are the same in principle as they were since the early days of C++.

You need the same template-head(s) grammar component(s), in the same order

template <typename T> requires std::is_integral_v<T>
template <typename U> requires std::is_integral_v<U>
const void Speaker<T>::speak(const Speaker<U> &speaker)
{
// code
}

The associated constraint expression and template <...> form the template-head together.


As an aside:

  • const qualified return types are redundant in the best case, or a pessimization in the worst case. I wouldn't recommend it (especially over void).
  • I'd also recommend using concepts (std::integral) if including the library header, not type traits (std::is_integral) that may or may not be included. The concepts allow for the abbreviated syntax, which is much more readable:
    template<std::integral T>
    template<std::integral U>

Concept for member function taking a template parameter in C++20

As far as I am aware, it is not possible to test for a template member function without evaluating any of the template parameters.

That said, if you have an idea of what the classification of inputs are allowed to be -- such as if a function can only be evaluated with integral-values or something like this -- then you can test it with an explicit instantiation which may be 'good enough' for whatever your purposes are:

template<typename U>
concept CREATOR=requires(U val) {
// Checks that 'val' has a 'fct' function template that works with integers
val.template fct<int>(5);
};

Live Example

In most cases, however, its generally more useful for a concept to be defined around the complete atomic definition required to fulfill its contract -- at which point its generally better to punt this evaluation off to a template argument as well:

template <typename T, typename U>
concept fnc_evaluatable = requires(T val, U in) {
val.template fct<U>(in);
};

And then use this in greater compositions.

At this point, the state of fnc being a template also becomes less important than the state of it having a member function call fct that is callable by a U -- and can likely be simplified to just:

template <typename T, typename U>
concept fnc_evaluatable = requires(T val, U in) {
val.fct(in);
};

How can unspecified types be used in C++20 'requires' expressions?

Something close to this can be accomplished by defining an adapter type that can implicitly convert to (almost) anything:

struct anything
{
// having both these conversions allows Foo's argument to be either
// a value, an lvalue reference, or an rvalue reference

template <typename T>
operator T&();

template <typename T>
operator T&&();
};

Note that these operators do not need to be implemented, as they will only be used in an unevaluated context (and indeed, they could not be implemented for all types T).

Then, HasFooMethod can be written as:

template <typename T>
concept HasFooMethod = requires(T t, anything a)
{
{ t.Foo(a) } -> std::same_as<void>;
};

Pass parameterized function and list of parameters using c++20 Concepts

You solve this by using the tools the standard library gives you. You use theinput_range and the invocable concepts, providing the range_reference_t type of the given range as the parameter to the invoked function.

template<typename Func, std::ranges::input_range Rng>
requires std::invocable<Func, std::ranges::range_reference_t<Rng>>
void bar(Func func, Rng params)
{
for(auto &¶m : params)
{
std::invoke(func, param);
}
}

Is there a way to make a concept that can represent a template parameter pack?

Yes, you can do this, which is functional equivalence to your example:

#include <concepts>
template<class T, std::same_as<T>... Ts>
void foo();

Can't make C++20 concept requiring bool member function

The concept needs to be written as:

template <typename T>
concept testable = requires(T t)
{
{ t.foo() } -> std::convertible_to<bool>;
};

Note that this is actually more explicit in the constraints for foo. It says, calling foo must return a type that is convertible to a bool.

This means you can also specify that the return should be exactly a bool, if that's what you want:

template <typename T>
concept testable = requires(T t)
{
{ t.foo() } -> std::same_as<bool>;
};


Related Topics



Leave a reply



Submit