如何在 C++20 'requires' 表达式中使用未指定的类型?
How can unspecified types be used in C++20 'requires' expressions?
我正在尝试编写一个 C++20 概念来表达类型具有特定方法的要求,该方法接受一个参数,但出于这个概念的目的,我不关心参数类型是什么是。
我试过写这样的东西:
template <typename T>
concept HasFooMethod = requires(T t, auto x)
{
{ t.Foo(x) } -> std::same_as<void>;
};
然而,gcc 和 clang 都拒绝了这个,给出了一个错误,即 'auto' cannot be used in the parameter list of a requires expression this way.
另一种方法是将 'x' 的类型作为第二个模板参数:
template <typename T, typename TX>
concept HasFooMethod = requires(T t, TX x)
{
{ t.Foo(x) } -> std::same_as<void>;
};
但是这需要在使用这个概念时明确指定 TX,无法推导:
struct S { void Foo(int); };
static_assert(HasFooMethod<S>); // doesn't compile
static_assert(HasFooMethod<S, int>); // the 'int' must be specified
有什么方法可以写出一个允许 Foo 接受 未指定 类型的参数的概念吗?
问题 非常相似,但不相同:该问题询问如何要求(模板化的)方法可以采用 any 满足给定的类型概念,而这个问题是关于要求方法采用 some 特定类型,尽管该类型未指定。在量词方面,另一个问题是询问(有界的)全称量化,而这个问题是关于存在量化的。另一个问题的答案也不适用于我的情况。
概念并非旨在提供您正在寻找的那种功能。 So they don't provide it.
概念旨在约束模板,指定模板打算在其定义中使用(或至少免费使用)的一组表达式或语句。
在你如此约束的模板中,如果你写表达式 t.Foo(x)
,那么你就知道 x
的类型。它可以是具体类型、模板参数或派生自模板参数的名称。无论哪种方式,x
的类型在受约束的模板中可用。
所以如果你想约束这样的模板,你同时使用t
的类型和x
的类型。那时两者都可用,因此创建这样的约束没有问题。也就是说,约束不是 T
作为孤立类型;它在 T
和 X
之间的关联上。
概念并不意味着在真空中工作,与约束的实际使用位置没有任何关联。您不应该专注于创建一元概念,以便用户可以 static_assert
他们 类 反对他们。概念不是为了测试一个类型是否满足它们(这基本上就是你的 static_assert
正在做的);它们用于限制使用它们的模板定义。
您的约束需要 FooCallableWith
,而不是 HasFooMethod
。
可以通过定义可以隐式转换为(几乎)任何东西的适配器类型来完成类似的事情:
struct anything
{
// having both these conversions allows Foo's argument to be either
// a value, an lvalue reference, or an rvalue reference
template <typename T>
operator T&();
template <typename T>
operator T&&();
};
请注意,这些运算符不需要实现,因为它们只会在未计算的上下文中使用(实际上,它们无法为所有类型 T 实现)。
那么,HasFooMethod
可以写成:
template <typename T>
concept HasFooMethod = requires(T t, anything a)
{
{ t.Foo(a) } -> std::same_as<void>;
};
我正在尝试编写一个 C++20 概念来表达类型具有特定方法的要求,该方法接受一个参数,但出于这个概念的目的,我不关心参数类型是什么是。
我试过写这样的东西:
template <typename T>
concept HasFooMethod = requires(T t, auto x)
{
{ t.Foo(x) } -> std::same_as<void>;
};
然而,gcc 和 clang 都拒绝了这个,给出了一个错误,即 'auto' cannot be used in the parameter list of a requires expression this way.
另一种方法是将 'x' 的类型作为第二个模板参数:
template <typename T, typename TX>
concept HasFooMethod = requires(T t, TX x)
{
{ t.Foo(x) } -> std::same_as<void>;
};
但是这需要在使用这个概念时明确指定 TX,无法推导:
struct S { void Foo(int); };
static_assert(HasFooMethod<S>); // doesn't compile
static_assert(HasFooMethod<S, int>); // the 'int' must be specified
有什么方法可以写出一个允许 Foo 接受 未指定 类型的参数的概念吗?
问题
概念并非旨在提供您正在寻找的那种功能。 So they don't provide it.
概念旨在约束模板,指定模板打算在其定义中使用(或至少免费使用)的一组表达式或语句。
在你如此约束的模板中,如果你写表达式 t.Foo(x)
,那么你就知道 x
的类型。它可以是具体类型、模板参数或派生自模板参数的名称。无论哪种方式,x
的类型在受约束的模板中可用。
所以如果你想约束这样的模板,你同时使用t
的类型和x
的类型。那时两者都可用,因此创建这样的约束没有问题。也就是说,约束不是 T
作为孤立类型;它在 T
和 X
之间的关联上。
概念并不意味着在真空中工作,与约束的实际使用位置没有任何关联。您不应该专注于创建一元概念,以便用户可以 static_assert
他们 类 反对他们。概念不是为了测试一个类型是否满足它们(这基本上就是你的 static_assert
正在做的);它们用于限制使用它们的模板定义。
您的约束需要 FooCallableWith
,而不是 HasFooMethod
。
可以通过定义可以隐式转换为(几乎)任何东西的适配器类型来完成类似的事情:
struct anything
{
// having both these conversions allows Foo's argument to be either
// a value, an lvalue reference, or an rvalue reference
template <typename T>
operator T&();
template <typename T>
operator T&&();
};
请注意,这些运算符不需要实现,因为它们只会在未计算的上下文中使用(实际上,它们无法为所有类型 T 实现)。
那么,HasFooMethod
可以写成:
template <typename T>
concept HasFooMethod = requires(T t, anything a)
{
{ t.Foo(a) } -> std::same_as<void>;
};