Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Requires clause positioning in C++20 function templates

In C++20, you can write a constrained function template in a couple of different ways:

template <typename T>
concept Fooable = true;

template <typename T>
    requires Fooable<T>
void do_something(T&); // (1)

template <typename T>
void do_something(T&) requires Fooable<T>; // (2)

According to the accepted answer in this question, these two forms are equivalent (which has always been my understanding).

However, I notice that GCC 12.1 considers (1) and (2) to be two different functions, rather than (2) being a redeclaration: it’s possible to provide definitions for both, and attempting to call do_something() is then ambiguous (example).

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

  • Is GCC correct, and these are two different functions?
  • If so, are there any technical differences in meaning between the two styles?

EDIT:

  • As pointed out in the comments, the linked questions states that a function template declaration and definition must use the same "requires style". What is the reason for this restriction?

(I vaguely recall from the Concepts TS days that requirements underwent "normalisation" to decide when they were equivalent — I guess this is no longer the case in C++20?)

>Solution :

The relevant rule here is [temp.over.link]/7-8:

  1. If the validity or meaning of the program depends on whether two constructs are equivalent, and they are functionally equivalent but not equivalent, the program is ill-formed, no diagnostic required.

  2. [Note 7: This rule guarantees that equivalent declarations will be linked with one another, while not requiring implementations to use heroic efforts to guarantee that functionally equivalent declarations will be treated as distinct.

// guaranteed to be the same
template <int I> void f(A<I>, A<I+10>);
template <int I> void f(A<I>, A<I+10>);

// guaranteed to be different
template <int I> void f(A<I>, A<I+10>);
template <int I> void f(A<I>, A<I+11>);

// ill-formed, no diagnostic required
template <int I> void f(A<I>, A<I+10>);
template <int I> void f(A<I>, A<I+1+2+3+4>);

end note]

In your example:

template <typename T>
    requires Fooable<T>
void do_something(T&); // (1)

template <typename T>
void do_something(T&) requires Fooable<T>; // (2)

These are functionally equivalent (they have the same constraints, basically) but not equivalent (the rules for equivalence, described earlier in this section, only allow for a very narrow set of differences – and writing constraints in different places is not one such allowable difference), which makes this ill-formed no diagnostic required. In practice, because they’re not equivalent, they’re different overloads – but because they’re functionally equivalent, any attempt to call would be ambiguous between them.

As I point out in the other answer, these have the same meaning – it’s just that you have to stick with one form for the declaration and the definition if you split them.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading