使用概念禁用非模板方法

Disable non-templated methods with concepts

是否有限制非模板化方法的语法?我在带有 clang 概念分支和 gcc 的 godbolt 上尝试过的所有语法都无法编译:

// 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() {}
};

使其编译的技巧与您在 SFINAE 中需要做的技巧相同,即制作方法模板,即使它们实际上不是模板。有趣的是,约束似乎不需要方法模板,它可以单独在 class 模板上正常工作,所以我真的希望有一种方法可以通过概念应用约束,而不必求助于老技巧:

// 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() {}
};

现实生活中的例子:

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>;

是的,有!! The requires clause 可以作为函数声明符的最后一个元素出现,在这种情况下它允许约束非模板方法(或与此相关的自由函数):

// This works as expected! Yey!!

template <class T, bool Copyable_buf = false>
struct Buffer
{
    Buffer(const Buffer& other) requires Copyable_buf
    {
        // ...
    }
};

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

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

这个答案是经验性的,基于对当前概念实现的测试。 Godbolt test. 给出了确认此行为的标准报价。

为了支持其他答案,这里是最新标准草案中关于此的规范性措辞:

[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 ]

如这两段所述,尾随的 requires 子句可以出现在函数声明符的末尾。它的意思是通过它接受作为参数(包括概念)的常量表达式来约束函数。

最近有关于此的更改。参见 https://github.com/cplusplus/nbballot/issues/374

它指出:

How constraints work with non-templated functions is still under heavy construction during this late stage in the process. While we have provided various comments that build in a direction where supporting such constructs (including ordering between multiple constrained functions based on their constraints) would become possible, we acknowledge that WG 21 might not find a solution with consensus in time for the DIS. We ask WG 21 to evaluate the risk of shipping the feature in such a state and consider removing the ability to declare such functions.

Does EWG want to consider this for C++20?

 |  F |  A |
 |----|----|
 | 16 |  0 |

Motion passes. Hubert to coordinate with CWG.

强调我的。

看来,受约束的非模板化函数现在已从 C++20 中删除。