首页 > 解决方案 > C++ 概念的通配符表示“接受此模板参数的任何内容”

问题描述

有没有办法允许一个concept带有模板参数的,可以接受提供的任何模板参数?

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

一个使用示例:

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

上面是必需的,因为不幸的是,原始类型的 行为与和的类类型不同。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) { /* */ }

不幸的是,在 C++20 中不允许 auto 作为模板参数占位符

所以Pair<auto, auto>暂时不是解决办法。


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

Python:

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

爪哇:

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

C++20 之前的语法看起来像1

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

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

预 C++20 代码

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


1关于模板的相关问题(C++ 20 之前):模板接受 C++ 中的“任何东西”

标签: c++templatesautoc++20c++-concepts

解决方案


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

让我们首先将Any声明为标签类,无需实现它。

class Any;

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

T匹配A

  • 如果AAny -- 或 --
  • 如果T == AT派生自A

如问题中所述,可以对类类型进行T == AT派生自A的检查,但原始类型需要添加对.std::derived_fromstd::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


推荐阅读