23

I was playing around with C++ concepts and function overloading in a context completely removed from any templates, and stumbled upon this:

struct S
{
    int mult(int x) requires (true)  { return x; }
    int mult(int x) requires (false) { return x * 2; }
};

int main()
{
    std::cout << S{}.mult(5) << std::endl;
}

g++ 11 refuses to compile this snippet because requires-clauses may not be attached to non-template functions.

clang++ 13 is, however, fine with this snippet, but surprisingly spits out 10 instead of the 5 that I expected.

I know that the C++20 standard has only recently come out of the oven, so I completely understand that there are many issues to be worked out regarding concepts.

Disregarding the apparent uselessness of constant literal concepts, my question is: Are programs with requires-clauses which are always false ill-formed, possibly without requiring a diagnostic? Or, perhaps, is what I have not even legal C++ at all, as g++ is saying?

0

3 Answers 3

17

These are the two first examples in the "trailing requires-clause" section of the standard:

void f1(int a) requires true;               // error: non-templated function

template<typename T>
  auto f2(T a) -> bool requires true;       // OK

Although examples given in the standard are explicitly non-normative, these make the intent abundantly clear.

Essentially, concepts can only be applied to template functions (so your code is invalid C++), but Boolean literals (e.g., requires true) are perfectly valid.

However, templates whose requirements are never satisfied (whether by a literal false in a required clause or otherwise) seem to be ill-formed, as indicated by this example,

Sign up to request clarification or add additional context in comments.

4 Comments

Maybe this rule indicates requires false is ill-formed?
It isn't clear why a specialisation with unsatisfied constraints would be considered invalid. (It isn't clear what an invalid specialisation is at all). There is an adjacent rule about constraint expressions that talks about well-formedness. If a constraint were considered a part of a specialisation, this rule presumably would not be needed.
This example seems to indicate again that requires false is ill-formed.
Yes indeed, missed that
16

Your snippet is not legal C++.

[dcl.decl.general]

4 The optional requires-clause ([temp.pre]) in an init-declarator or member-declarator shall be present only if the declarator declares a templated function ([dcl.fct]). When present after a declarator, the requires-clause is called the trailing requires-clause. [...]

Since there isn't a template in sight (and no room to discuss if the mult overloads are templated functions), your attempt is plain ill-formed. It's a "shall" requirement of a diagnosable rule.

So GCC is correct. Clang is doubly wrong to call a completely unintuitive overload. Though it really went of the rails when it accepted the program to being with.

5 Comments

Thanks for your input! I was hoping that I somehow could enable or disable specific overloads based on some macros with evidently compile-time evaluatable expressions, but that's a story that has run its course now. I would argue that this is a perfectly intuitive overload just by looking at the code, but since it's not legal anyway...
@CoffeeTime - If you are already using macros, you can remove the overloads entirely from the source, rather than wait for overload resolution to ignore them. Trailing requirements exist to manipulate the member functions of class templates (since it's the only place another requirement may be tacked on).
Yes, about that... I would! If only I found a way then to remove overloads from source at compile-time and not at preprocessing time... :)
@CoffeeTime - I don't follow why preprocessing time would be bad, but it's possible I did not understand what you are doing. It may be a subject for another Q&A.
I always imagine clang is named for the sound it makes when it goes off-rails like this XD
2

As n. 1.8e9-where's-my-share m.'s answer says, concepts can only be used with templates. So your example is ill-formed.

What if a requires-clause which are always false appears in a template? According to [temp.res.general]/6.1:

The program is ill-formed, no diagnostic required, if:

  • no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated, or ...

It is still ill-formed, but no diagnostic required. Example 11 in [temp.res.general] also confirms this.

[Example 11:

template<typename T> struct S1 {
  template<typename U>
    requires false
  struct Inner1;                // ill-formed, no diagnostic required
};

template<typename T> struct S2 {
  template<typename U>
    requires (sizeof(T[-(int)sizeof(T)]) > 1)
  struct Inner2;                // ill-formed, no diagnostic required
};

The class S1<T>​::​Inner1 is ill-formed, no diagnostic required, because it has no valid specializations. S2 is ill-formed, no diagnostic required, since no substitution into the constraints of its Inner2 template would result in a valid expression. — end example]

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.