【问题标题】:C++ Concepts - Concept in the requires parenthesis causes 2 conflicting error messagesC++ 概念 - 需要括号中的概念导致 2 个冲突的错误消息
【发布时间】:2020-09-15 14:11:48
【问题描述】:

今天早上我努力工作以更多地理解概念(仍然是新手),但我偶然发现了一些奇怪的东西。

我想了解好的概念如何与 Lambda 函数一起使用,我尝试了很多次。

让我们从这段代码开始(有效!):

#include <concepts>
#include <stdio.h>

template <typename T>
concept Integral =
requires(T n)
{
    { n } -> std::convertible_to<int>;
};

template <typename Operation>
concept CalculatorOperation =
requires(Operation&& op, int a, int b)
{
    { op(a, b) } -> Integral;
};

template <CalculatorOperation Operation>
static inline constexpr Integral auto Calculator(const Integral auto First,
                                                 const Integral auto Second)
{
    return Operation{}(First, Second);
}

int main()
{
    static constexpr auto Multiply = [](const Integral auto First,
                                        const Integral auto Second)
                       { return First * Second; };
    return Calculator<decltype(Multiply)>(15, 18);
}

我创建了一个 Integral 概念,如果类型可转换为 int,则该概念(只是为了假人)得到满足。 我创建了一个计算器操作概念,它接受一个 lambda、两个整数,并确保该操作返回一个 Integral。

我决定要将概念中的 int 变成 Integral,因为我不'不希望只接受 int 的操作(我想要所有整数类型)。

所以我改变了

requires(Operation&& op, int a, int b)

requires(Operation&& op, Integral&& a, Integral&& b)

这不仅对我不起作用,而且实际上我收到了 2 条相互冲突的错误消息(在 GCC 10.1 上):

<source>:13:30: error: placeholder type not allowed in this context


<source>:13:30: error: expected 'auto' or 'decltype(auto)' after 'Integral'

   13 |     requires(Operation&& op, Integral&& a, Integral&& b)

我使用 GodBolt 的网站 Compiler Explorer 运行了这个。 这里发生了什么?有没有实际的方法可以做到这一点?

【问题讨论】:

  • @HolyBlackCat -std=c++20 -O3
  • 你看到我说我改变了什么的问题的其余部分了吗?
  • 不,我没有。我的错。
  • Clang 给出了更好的错误信息:error: 'auto' not allowed in requires expression parameter。这是有道理的,因为在我看来,requires-parameters 只是std::declval 的一个花哨的替代品。您是否期望Integral auto 尝试每种整数类型直到适合?它只是不能那样工作。
  • 我是这样写的:gcc.godbolt.org/z/9eGpUz 还要注意你的Integral 接受很多不同的类型(浮点数;一些类),最好使用std::integral

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


【解决方案1】:

首先,通过这样做:

requires(Operation&& op, Integral&& a, Integral&& b)

你可能真的是这个意思:

requires(Operation op, Integral auto a, Integral auto b)

也就是说,使用 type-constraint auto 形式的占位符类型说明符。

然而,auto 参数产生函数模板,而 auto 占位符等效于该函数模板的实际发明的类型模板参数,因此它只能出现在从初始化程序进行类型推导的上下文中,这意味着:

void foo(auto x);

相当于:

template <typename T>
void foo(T x);

同样,

void bar(Integral auto x);

相当于:

template <Integral T>
void bar(T x);

这种转换不适用于 requires 表达式,即没有可以推断其类型的初始化程序,因此不能在需求参数列表的参数声明中使用 auto 占位符。正如标准所说,这些参数“仅用作定义需求的符号。”因此,您需要明确指定概念工作所需的所有模板参数,例如:

template <typename Operation, typename Lhs, typename Rhs>
concept CalculatorOperation = requires(Operation op, Lhs a, Rhs b)
{
    { op(a, b) } -> Integral;
};

template <typename Operation>
constexpr Integral auto Calculator(Integral auto first, Integral auto second)
    requires CalculatorOperation<Operation, decltype(first), decltype(second)>
{
    return Operation{}(first, second);
}

还请注意,我从需求参数列表中的每个参数声明中删除了&amp;&amp;。整个 requires 表达式是一个未计算的上下文,实际上没有传递任何参数

【讨论】:

  • 谢谢,这使概念范围和实际功能范围之间的区别更加清晰!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-07-27
  • 1970-01-01
  • 1970-01-01
  • 2016-08-25
  • 1970-01-01
  • 2017-02-27
  • 1970-01-01
相关资源
最近更新 更多