Satisfied and modeled concept?

Viewed 96

Introduction

The standard specifies that each concept is related to two predicates:

  1. predicate "is statisfied by": a concept is satisfied by a sequence of template argument when it evaluates to true. This is almost a syntactic check.
  2. predicate "is modeled by": A sequence Args of template arguments is said to model a concept C if Args satisfies C ([temp.constr.decl]) and meets all semantic requirements (if any) given in the specification of C. [res.on.requirements]

For some concepts, the requirements that makes a satisfied concept modeled are clearly expressed. Example [concept.assignable]

LHS and RHS model assignable_­from<LHS, RHS> only if

  • addressof(lhs = rhs) == addressof(lcopy)
  • [...]

But I wonder if the syntactic requirements also implicitly implies semantic requirements.

Question

Does the syntactic predicates implicitly imply requirement for the concept to be modeled ?

I see two kind of implicit requirement:

  1. The concept is satisfied because syntactically checked expressions are unevaluated expressions and such expressions would result in the program being ill-formed if those expressions were not unevaluated expressions.
  2. The concept is satisfied because syntactically checked expressions are not evaluated but evaluation of those expression would result in the program having undefined behavior.

Examples

For example, let's consider the default_initializable concept, defined here: [concept.default.init].

default_initializable is satisfied by A<int> but the program is ill-formed if a variable of type A<int> is default-initialized (demo):

template <class T>
struct A { 
    A() {
       f(T{});
       }
    };

static_assert (default_initializable <A<int>>);  // A<int> satisfies default_initializable

A<int> a{}; //compile time error: f not declared in this scope

default_initializable is satisfied by A but default-initialization of A result in undefined behavior (when the default-initialization is not preceded by a zero-initialization) (demo):

struct A { 
    int c;
    A() {
       c++;
       }
    };

static_assert (default_initializable <A>); // A satisfies default_initializable

auto p = new A;  //undefined behavior: indeterminate-value as operand of operator ++
1 Answers

a concept is satisfied by a sequence of template argument when it evaluates to true. This is almost a syntactic check.

No, it is not "almost" anything: it is a syntactic check. The constraints specified by a requires clause (for example) verify that a specific syntax is legal syntax for that type. This is all that "satisfying a concept" means.

Does the syntactic predicates implicitly imply requirement for the concept to be modeled?

... no. If satisfying a concept also implied modeling the concept, then the standard wouldn't need different terms for these.

The point of having such a distinction is the recognition that the concept language feature can't specify every requirement that concepts as a concept should encapsulate. So satisfying-a-concept is just the language part, while modelling-a-concept includes things that the language can't do.

But that question is kind of separate from what your two examples show. Your examples represent the difference between "valid syntax" and "can be compiled/executed". Satisfying a concept only cares about the former. And modelling a concept only cares about the latter to the extent that said semantic behavior is explicitly specified.

There is nothing in the standard about implicit semantic requirements. There is no statement to the effect of "all expressions/statements in a concept must be able to be compiled and/or executed in order for it to be modeled". Nor is it intended to.

However much we try to pretend it's more than this, concepts as it exists in C++20 is nothing more than a more convenient mechanism for performing SFINAE. SFINAE can't test compilable/executable validity of the contents of some expression, so neither can concepts. And neither does concepts attempt to pretend that it can.

Related