C++ 概念的通配符表示 "accepting anything for this template argument"

Wildcard for C++ concepts saying "accepting anything for this template argument"

有没有办法让带有 模板参数 concept 与提供的任何模板参数兼容?

即模板参数占位符的某种通配符魔术

用法举例:

template<class Me, class TestAgainst>
concept derived_from_or_same_as = 
    std::same_as<Me, TestAgainst> ||
    std::derived_from<Me, TestAgainst>;

以上是必需的,因为不幸的是 原始类型 behave differentlyclass 类型 for is_base_ofderived_from.

现在我们可以定义一个 Pair concept 来检查提供的类型:

template<class P, class First, class Second>
concept Pair = requires(P p) {
    requires derived_from_or_same_as<decltype(p.first), First>;
    requires derived_from_or_same_as<decltype(p.second), Second>;
};

用例 [a] - 接受任何有效的 As 对或 As 的子类型:

// this works well
void doWithPairOfA(const Pair<A, A> auto& p) { /* */ }

用例[b] - 接受任何有效对,对内部类型没有限制:

// this is *pseudo code* as Pair<auto, auto> is not allowed
void doWithAnyPair(const Pair<auto, auto> auto& p) { /* */ }

不幸的是,

所以Pair<auto, auto>暂时不是解决方案。


其他语言在某种程度上允许这样的语法,虽然与此处要求的语义和含义不同,但用法看起来非常相似。

Python:

// Any as wildcard
foo(lst: List[Any]) -> List[str]

Java:

// '?' as wildcard
List<String> foo(List<?> lst)

C++20 之前的语法类似于1:

用例 [a] - 尝试 接受任何有效的 As 对或 As 的子类型=64=]作为:

// not as good as with concepts above, this allows only "pair" of A and A
// **but rejects sub-types of A, which is not good**
// and there is no check that this is actually a pair (can be added with SFINAE)
template<template<class, class> typename PAIR>
void doWithPairOfA(const PAIR<A, A>& p) { /* */ }

用例[b] - 接受任何有效对,对内部类型没有限制:

// not as good as we would wish - we do allow any kind of "pair"
// but there is no check that this is actually a pair (can be added with SFINAE)
template<template<class, class> typename PAIR, typename ANY1, typename ANY2>
void doWithAnyPair(const PAIR<ANY1, ANY2>& p) { /* */ }

Pre C++20 code

概念能否提供更好的解决方案?


1 关于模板的相关问题(C++20 之前):Templates accepting “anything” in C++

您可以通过修改 Pair concept 来实现 通配符行为 接受并检查标签类型 Any.

我们先把Any声明为标签class,不用实现了

class Any;

现在我们可以创建一个 type_matches concept 来检查类型 T 匹配给定类型 A,规则如下:

T 匹配 A

  • 如果A任何 -- 或 --
  • if T==A 或者如果 T 来自 A

如问题中所述,检查 T==AT派生自 A 可以针对 class 类型 仅使用 std::derived_from 但是 原始类型 需要为 std::same_as 添加测试。

通配符匹配可通过以下代码实现:

template<class Me, class TestAgainst>
concept type_matches =
    std::same_as<TestAgainst, Any> ||
    std::same_as<Me, TestAgainst>  ||
    std::derived_from<Me, TestAgainst>;

Pair 概念将修改为:

template<class P, class First, class Second>
concept Pair = requires(P p) {
    requires type_matches<decltype(p.first), First>;
    requires type_matches<decltype(p.second), Second>;
};

代码现在可以允许两个必需的用例。

用例 [a] - 接受任何有效的 As 对或 As 的子类型:

// can be called with a Pair of As or sub-type of As
void doWithPairOfA(const Pair<A, A> auto& p) { /* */ }

用例[b] - 接受任何有效对,对内部类型没有限制:

void doWithAnyPair(const Pair<Any, Any> auto& p) { /* */ }

代码:https://godbolt.org/z/higX9f