C++ Logo

std-discussion

Advanced search

Re: Question regarding how to declare templated functions inside a concept declaration

From: Marios Staikopoulos <marios_at_[hidden]>
Date: Fri, 29 Apr 2022 20:09:05 +0000
Okay I see, but to me this is really our (the programmers) fault, it should have been a requirement of Iterator to be move assignable/copyable in the first place – the code we have written has made implicit assumptions, when the Iterator concept should have been more explicit in its requirements.

If I understand correctly the issue is more of that IterationAlgorithm as a concept doesn’t actually know if Iterator is copyable, because such a constraint was not specified, yet we invoked a copy/move of iterator in the definition of apply.

I think the simple solution would be to have it check if Iterator defines such a constraint, and if not simply error – something like an error saying, “Expected Iterator to be copyable, but no such constraint was defined”. After all, we are implying a constraint on a type, and it should be trivial to check that we satisfy that constraint. I would think that would be better than just lazy-evaluating much later.

From: Edward Catmur <ecatmur_at_[hidden]>
Sent: Friday, April 29, 2022 12:49 PM
To: Marios Staikopoulos <marios_at_[hidden]>
Cc: std-discussion_at_[hidden]
Subject: Re: [std-discussion] Question regarding how to declare templated functions inside a concept declaration

On Fri, 29 Apr 2022 at 19:47, Marios Staikopoulos <marios_at_[hidden]<mailto:marios_at_[hidden]>> wrote:
I’m not sure if there is a misunderstanding, but the IterationAlgorithm concept, and other dependent concepts, need only to check that the function signatures are conforming to the requirements, not that they compile correctly – that error checking regarding movability and other constraints will happen during compilation normally, ie:

struct IterationAlgorithm_Impl
{
    template<Iterator T>
    void apply(T begin, T end) {
        // …. Do stuff here
    }
};

// Verify that our implementation satisfies the concept
static_assert(InterationAlgorithm<IterationAlgorithm_Impl>);

> Even for constraints expressed entirely syntactically, there's a wider problem which is that a compiler given the task of constructing archetypes is *required* to do so in the laziest, most infuriatingly pettifogging way possible, You specified that { cbuf.HasData() } -> std::same_as<bool>; for cbuf T const, which is fine, but that doesn't say anything about being able to call HasData on a T lvalue. Or a T or T const prvalue or xvalue. Or if your Iterator is random-access and thus addable with int, then it's obvious to humans that it should also be addable with unsigned, long etc., but not to a compiler.

In concept IterationAlgorithm, we don’t need to know that the code compiles correctly or satisfies the constraints at the declaration of the concept.. that isn’t its responsibility – its responsible only for verifying that the proper signatures required exist. In this case, all the IterationAlgorithm wants to check for is that there is a member function apply that takes in any Iterator concept as arguments.

Right, and that IterationAlgorithm_Impl does not satisfy IterationAlgorithm, since it *does not have such a member function*. Instead it takes arguments that satisfy Iterator *and are movable*.

Whether or not the apply() uses the constraints, and semantics of Iterator correctly will happen when IterationAlgorithm_Impl is compiled.

I'm not talking about the internals of apply(); those are irrelevant.

Suppose your Iterator concept is:

template<class T> concept Iterator = requires(T t) {
    { ++t } -> std::same_as<T&>;
};

Then:

struct EvilIterator {
    EvilIterator& operator++() &;
    ~EvilIterator() = delete;
};
static_assert(Iterator<EvilIterator>);

But:

struct IterationAlgorithm_Impl {
    template<Iterator T>
    void apply(T begin, T end) {
        // …. Do stuff here
    }
};
template<class A>
concept IterationAlgorithm = requires(A a, EvilIterator it) {
    { a.apply(it, it) } -> std::same_as<void>;
};
static_assert(IterationAlgorithm<IterationAlgorithm_Impl>);

The latter static_assert fails because the concept IterationAlgorithm is *implicitly* requiring that the Iterator type is copyable.

And in practice, much of the time you will have additional implicit constraints in the form of (deduced or) calculated return types, along with potentially specialized class and variable templates.

Received on 2022-04-29 20:09:09