Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does C++20's requires expression not behave as expected?

#include <type_traits>

template<typename T>
struct IsComplete final
    : std::bool_constant<requires{sizeof(T);}>
{};

int main()
{
    struct A;
    static_assert(!IsComplete<A>::value); // ok

    struct A{};
    static_assert(IsComplete<A>::value);  // error
}

I expected that the second static_assert should be true as A is a complete type now.

Why does C++20's requires expression not behave as expected?

like image 767
xmllmx Avatar asked May 23 '21 09:05

xmllmx


People also ask

What does expected an expression mean in C?

Expected expression. This error is produced whenever the compiler is expecting an expression on the line where the error occurred. In the following example, the trailing comma in the initializer indicates to the compiler that another expression will follow.

What is expected expression before INT in C?

The answer lies in the grammar of the if statement, as defined by the C standard. The relevant parts of the grammar I've quoted below. Succinctly: the int b = 10 line is a declaration, not a statement, and the grammar for the if statement requires a statement after the conditional that it's testing.

What is a requires expression in C++?

Requires expressions The keyword requires is also used to begin a requires-expression, which is a prvalue expression of type bool that describes the constraints on some template arguments. Such an expression is true if the constraints are satisfied, and false otherwise:

What's new in c++20?

The C++20 standard added constraints and concepts to the language. This addition introduced two new keywords into the language, concept and requires. The former is used to declare a concept, while the latter is used to introduce a requires expression or a requires clause.

What is a requires clause in C++?

A requires clause is a way to specify a constraint on a template argument or function declaration. The requires keyword must be followed by a constant expression. The idea is though, that this constant expression should be a concept or a conjunction/disjunction of concepts.

Can a requirement start with an unparenthesized requires-expression?

The expression is an unevaluated operand; only language correctness is checked. A requirement that starts with the keyword requires is always interpreted as a nested requirement. Thus a simple requirement cannot start with an unparenthesized requires-expression.


1 Answers

It's a wrong expectation. To start with, a class template has only one point of instantiation in a translation unit:

[temp.point]

7 ... A specialization for a class template has at most one point of instantiation within a translation unit. A specialization for any template may have points of instantiation in multiple translation units. If two different points of instantiation give a template specialization different meanings according to the one-definition rule, the program is ill-formed, no diagnostic required.

Templates never allowed for two points in the program to have a different interpretation of the template for the same set of arguments (an ODR nightmare in the general case). You basically start venturing into nasal-demon territory with your attempt at the trait.

And should you think using C++20 concepts is gonna change anything, you'll dive right into ill-formed; no diagnostic required territory if you conceptify the example

template<typename T>
concept IsComplete = requires{sizeof(T);};

int main()
{
    struct A;
    static_assert(!IsComplete<A>); // ok

    struct A{};
    static_assert(IsComplete<A>);  // error or nuclear launch.
}

[temp.names]

8 ... A concept-id evaluates to true if the concept's normalized constraint-expression is satisfied ([temp.constr.constr]) by the specified template arguments and false otherwise.

[temp.constr.atomic]

3 ... If, at different points in the program, the satisfaction result is different for identical atomic constraints and template arguments, the program is ill-formed, no diagnostic required.

It's not anything new, concepts just add more of the same. A template's meaning for a specific set of arguments must not change if some property of the arguments is different in two different points in the program.

So while a concept (even in pre-C++20 SFINAE hackery) that checks if a type is complete may be written, to use it carelessly is to play with fire.

like image 146
StoryTeller - Unslander Monica Avatar answered Oct 23 '22 15:10

StoryTeller - Unslander Monica