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

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

本文关键字:C++ 通配符 argument template accepting anything for this 表示      更新时间:2023-10-16

是否有方法允许具有模板参数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基元类型的行为与

类类型现在,我们可以定义一个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]

Java:

// '?' 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++中接受"任何东西">

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

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

class Any;

现在,我们可以创建type_matchesconcept来检查类型T是否与给定类型a匹配,规则如下:

T匹配A

  • 如果A任何--或--
  • 如果T=A,或者如果T是从A导出的

如问题中所述,对T=AT的检查是从派生的。可以仅使用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>;

配对概念将修改为:

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