Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Disable non-templated methods with concepts

Is there a syntax to constraint a non-templated method? All the syntaxes I've tried on godbolt with clang concepts branch and gcc fail to compile:

// these examples do not compile

template <bool B>
struct X
{
    requires B
    void foo() {}
};

template <class T>
struct Y
{
    requires (std::is_trivially_copyable_v<T>)
    auto foo() {}
};

The trick to make it compile is the same trick you needed to do with SFINAE, make the methods template, even though they really are not templates. And funny enough, the constraint doesn't seem to need the method template, it can work fine on the class template alone, so I really hope there is a way to apply the constraints with concepts without having to resort to the old hacks:

// old hacks

template <bool B>
struct X
{
    template <bool = B>
    requires B
    auto foo() {}
};

template <class T>
struct Y
{
    template <class = T>
    requires std::is_trivially_copyable_v<T>
    auto foo() {}
};

Real life example:

template <class T, bool Copyable_buf = false>
struct Buffer
{
    /* ... */

    requires Copyable_buf
    Buffer(const Buffer& other)  {}

    /* ... */
};

template <class T>
using Copyable_buffer = Buffer<T, true>;
like image 409
bolov Avatar asked Dec 10 '22 01:12

bolov


1 Answers

To support the other answer on this, here is the normative wording about this, from the latest standard draft:

[dcl.decl]

1 A declarator declares a single variable, function, or type, within a declaration. The init-declarator-list appearing in a declaration is a comma-separated sequence of declarators, each of which can have an initializer.

init-declarator-list:
    init-declarator
    init-declarator-list , init-declarator
init-declarator:
    declarator initializeropt
    declarator requires-clause

4 The optional requires-clause ([temp]) in an init-declarator or member-declarator shall not be present when the declarator does not declare a function ([dcl.fct]). When present after a declarator, the requires-clause is called the trailing requires-clause. The trailing requires-clause introduces the constraint-expression that results from interpreting its constraint-logical-or-expression as a constraint-expression. [ Example:

void f1(int a) requires true;               // OK
auto f2(int a) -> bool requires true;       // OK
auto f3(int a) requires true -> bool;       // error: requires-clause precedes trailing-return-type
void (*pf)() requires true;                 // error: constraint on a variable
void g(int (*)() requires true);            // error: constraint on a parameter-declaration

auto* p = new void(*)(char) requires true;  // error: not a function declaration

— end example ]

As those two paragraphs specify, a trailing requires clause can appear at the end of function declarators. Its meaning is to constrain the function by the constant expression it accepts as an argument (which includes concepts).

like image 134
StoryTeller - Unslander Monica Avatar answered Jan 24 '23 16:01

StoryTeller - Unslander Monica