概念:检查带有参数的方法的签名

Concepts: checking signatures of methods with arguments

我一直在研究概念。这是一个最小的例子,我试图根据方法签名创建一个概念:

template<typename T>
concept bool myConcept() {
    return requires(T a, int i) {
        { a.foo()   } -> int;
        { a.bar(i)  } -> int;
    };
}

struct Object {
    int foo()    {return 0;}
    int bar(int) {return 0;}
};

static_assert(myConcept<Object>(), "Object does not adhere to myConcept");

令我惊讶的是,写 { a.bar(int) } -> int 并没有奏效,所以我求助于向 requires 表达式添加一个额外的参数。这看起来有点奇怪,我想知道是否有办法做同样的事情。另一件有用的事情是使用类似 { a.bar((int)0) } -> int 的东西,但我发现这更糟。

概念检查表达式,a.bar(int)不是一个。通过写作

{ a.foo(int) } -> int

您要求编译器检查上述表达式的类型是否为 int。这没有意义。

您确实找到了有效的替代方案;另一个可能是,因为 a.bar(x) 的类型不依赖于 x' 值:

template<typename T>
concept bool myConcept() {
    return requires(T a) {
        { a.foo()   } -> int;
        { a.bar(0)  } -> int;
    };
}

struct Object {
    int foo()    {return 0;}
    int bar(int) {return 0;}
};

static_assert(myConcept<Object>(), "Object does not adhere to myConcept");

由于类型的实际值无关紧要,我建议使用 int{} 作为参数。这记录了争论的目的,IMO:

{ a.bar(int{})  } -> int;

显然这不适用于没有默认构造函数的类型。在模板中,人们会使用 std::declval 来解决类似的问题,但这里 GCC 错误:

error: static assertion failed: declval() must not be used!

但是没有什么可以阻止我们编写与概念一起使用的等效(但未实现)函数,如下所示:

#include <type_traits>

template <class T>
typename std::add_rvalue_reference<T>::type makeval();

template<typename T>
concept bool myConcept() {
    return requires(T a, int i) {
        { a.foo() } -> int;
        { a.bar(makeval<int>()) } -> int;
    };
}

struct Object {
    int foo()    {return 0;}
    int bar(int) {return 0;}
};

static_assert(myConcept<Object>(), "Object does not adhere to myConcept");