编写检测泛型成员的函数

Writing a function that detects a generic member

现在使用 C++20 可以编写模板函数 detect_foo,它将 return 模板参数是否为具有名为 foo 的成员的结构。

示例:

consteval bool detect_foo(auto&& arg) {
  if constexpr(requires { arg.foo; }) {
    // arg has a "foo" member
    return true;
  } else {
    // arg does not
    return false;
  } 
}

因此,我们在 . 运算符的左侧具有泛型:在 arg.foo 中,arg 可以是泛型。

我的问题是:如何使 foo 部分通用,例如我们有没有一种非宏的方法来编写一个 detect 函数,它的 API 离这个不远:

bool has_the_member = detect(some_struct, name_of_a_member);

例如

struct foo { int value; } a_foo;
struct bar { int value; } a_bar;

detect(a_foo, /* some magic incantation to refer to "value" */);
detect(a_bar, /* should be the same incantation than for a_foo */);

您不需要 detect 函数,因为您可以只使用 requires.

have M optional fields ... want to perform the same logic ... and only write that logic once

想到的传递“成员名称”的唯一方法是使用 returns 表示成员的 lambda(或函数):

auto get_foo = [](auto &&obj) -> decltype(decltype(obj)(obj).foo) {return decltype(obj)(obj).foo;};

注意明确指定的 return 类型,这给了我们 SFINAE。
请注意,您必须重复成员名称两次。这需要一个宏来生成那些 lambda。

您仍然不需要 detect 函数,并且可以直接在您的代码中将 requires 与 lambda 一起使用。示例:

void print_member(const auto &obj, auto member)
{
    if constexpr (requires{member(obj);})
        std::cout << member(obj) << '\n';
    else
        std::cout << "No such member.\n";
}

struct A {int foo = 1;};
struct B {int bar = 1;};

int main()
{
    print_member(A{}, get_foo); // `1`
    print_member(B{}, get_foo); // `No such member.`
}