【问题标题】:C++ concept for compound assignment operators复合赋值运算符的 C++ 概念
【发布时间】:2020-05-23 12:36:31
【问题描述】:

我有一个普通二元运算符的概念

template<typename Op, typename T> concept is_binary_operation =
    requires (const T& t1, const T& t2) // e.g. a+b
{
    {Op()(t1,t2)}->std::convertible_to<T>;
};

以及复合赋值运算符的概念

template<typename Op, typename T> concept is_operation_and_assign =
   requires (T& t1, const T& t2) // e.g a += b;
{
  {Op()(t1,t2)}->std::convertible_to<T>;
};

对于复合赋值运算符,这可以按预期工作:

template<typename T> struct op_and_assign
{
    T& operator()(T& t1, const T& t2)
    {
        t1 += t2;
        return t1;
    }
};

这个“is_operation_and_assign”而不是“is_binary_operation”

std::cout << is_binary_operation<op_and_assign<double>, double> << " ";
std::cout << is_operation_and_assign<op_and_assign<double>, double> << std::endl;

打印“0 1”。然而,std::plus 满足这两个概念:

std::cout << is_binary_operation<std::plus<double>, double> << " ";
std::cout << is_operation_and_assign<std::plus<double>, double> << std::endl;

打印“1 1”。

我必须如何更改“is_operation_and_assign”的概念,以便获得输出“1 0”,即它会由 op_and_assign 而不是由 std::plus 实现?

为了更清楚我需要什么:我有两个版本的算法,一个使用复合赋值运算符,一个使用二元运算符:

template<typename Op, typename T>
int f() requires is_operation_and_assign<Op, T>
{   
    return 0;
}
template<typename Op, typename T>
int f() requires is_binary_operation<Op, T>
{ 
    return 1;
}

我可以调用 op_and_assign 的版本

f<op_and_assign<double>, double>();

但 std::plus 的版本

f<std::plus<double>, double>();

不编译。 (错误:对“f”的调用不明确)

更新:同时我找到了解决方法:

当我简单地将&amp;&amp; !is_binary_operation&lt;Op, T&gt; 添加到第一个f 时:

template<typename Op, typename T>
int f() requires (is_operation_and_assign<Op, T>
               && !is_binary_operation<Op, T>)
{ 
    return 0;
}
template<typename Op, typename T>
int f()  requires is_binary_operation<Op, T> 
{ 
   return 1;
}

那么第二个调用就不再模棱两可了,即两者都

f<op_and_assign<double>, double>();
f<std::plus<double>, double>();

编译(并选择所需的函数)。

【问题讨论】:

  • 另一方面,如果您在定义中使用模板模板参数,例如template &lt;template &lt;typename T&gt; typename op&gt; concept 。使用概念时必须编写两次模板类型看起来很尴尬。
  • @Mansoor:不等价,检查类型似乎更自然,is_binary_operation&lt;std::plus&lt;void&gt;, double&gt; 也有意义。
  • 你是什么意思但不是std::plus?可以加两个doubles,怎么不实现呢?
  • @Barry:我认为 OP 想检查 operator()签名

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


【解决方案1】:

澄清你的概念实际上在检查什么很重要,因为它不是你想象的那样。

这个:

template<typename Op, typename T> concept is_operation_and_assign =
   requires (T& t1, const T& t2) // e.g a += b;
{
  {Op()(t1,t2)}->std::convertible_to<T>;
};

检查您是否可以使用T&amp;T const&amp; 调用Op()(t1, t2),并且您得到满足convertible_to&lt;T&gt; 的内容。当您提供时:

template<typename T> struct op_and_assign
{
    T& operator()(T& t1, const T& t2)
    {
        t1 += t2;
        return t1;
    }
};

作为第一个模板参数,它实际上检查了什么?这是一个未计算的表达式,我们正在检查是否可以调用op_and_assign&lt;T&gt;()。我们没有评估呼叫操作员的主体,我们只是检查它是否是一个有效的呼叫。所以这和我们写的没什么不同:

template<typename T> struct op_and_assign
{
    T& operator()(T& t1, const T& t2);
};

它没有被评估,没有实体,所以唯一重要的是约束。这里没有约束,所以只要参数是可转换的,op_and_assign 总是可以调用的。

当你这样做时:

is_binary_operation<op_and_assign<double>, double>

您实际上是在询问是否可以适当地转换参数。对于is_binary_operation,您提供了两个double const&amp; 类型的参数(来自您的require 表达式),但op_and_assign&lt;double&gt; 需要一个double&amp;。这就是为什么此特定检查不起作用的原因。


关于如何修复它。 op_and_assign 应该看起来像这样:

struct op_and_assign
{
    template <typename T, typename U>
    auto operator()(T&& t, U&& u) const -> decltype(t += u);
};

现在我们实际上正在检查是否可以执行+=

但这不会改变你不能分配给double const&amp;。即使您没有进行您打算进行的检查,您也会在那里得到正确的答案。

【讨论】:

  • struct op_and_assign 不检查任何东西;它是一个类似 std::plus 的函数对象。你也可以用不同的方式来问这个问题:我如何写两个概念来区分 op_and_assign 和 std::plus?
  • @Helmut 我不明白你在问什么。你已经在区分它们了。怎么区分?
  • 问题是对于 op_and_assign 和 std::plus,is_binary_operation 是正确的。在上面的例子中,编译器说对 f 的调用是模棱两可的。
  • @HelmutZeisel 什么? is_binary_operation&lt;op_and_assign&lt;double&gt;, double&gt; 绝对是假的。
  • @HelmutZeisel 当然,对f 的调用是模棱两可的,两个约束条件都成立。你想在这里做出什么区分? plus&lt;double&gt; 可以用 const 左值和非 const 左值调用...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-11-16
  • 2020-04-17
  • 2012-08-15
  • 1970-01-01
  • 2020-12-01
  • 2020-03-23
  • 1970-01-01
相关资源
最近更新 更多