自引用C ++ 20概念

时间:2020-05-20 18:51:01

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

与下面的无效代码等效的道德是什么?

// Suppose you want to check, as part of a concept,
// if some operation on a type results in a type that models such concept.
// you cannot constrain the resulting type with the same concept
// in the same way you can't have self referential types
// (that would result in recursive definitions)

template<class T>
concept Int = requires(const T& a, const T& b) {
  { a + b } -> Int; // compiler don't know what to do
};

2 个答案:

答案 0 :(得分:4)

假设作为概念的一部分,您要检查对某个类型的某些操作是否导致对该概念进行建模的类型。

那是无限的递归。像任何函数递归一样,您必须具有终止条件。定义模板参数终止条件的通常方法是通过专门化。但是concept明确地 不能被专门化,因此不会有终止条件。

从逻辑上讲,它也是不连贯的,因为您试图使用要定义的东西来编写定义。定义上没有意义的东西没有“道德等同物”。

您的概念似乎是在说:“ T是我可以添加到另一个T中并产生……的东西”,这是什么?您是否希望它能够产生一些不相关的类型U,并且可以将其添加到另一个U中来产生...又一次呢?甚至不理会这个问题,是否应该将U添加到T中?如果是这样,那应该产生什么?

在编写概念时,从用例开始,首先确定要执行的操作。

答案 1 :(得分:3)

可以进行这种递归模板检查,但是这会使代码难以阅读。 原理是将递归模板检查转发给通过依存名称查找找到的函数,仅当类型不属于已检查类型的列表时,才对其约束进行验证...如果类型已属于已检查类型的列表...选中类型,则该函数被SFINAE禁用,而另一个未递归引用该概念的函数是通过重载分辨率选择的:

查看实际情况:compiler-explorer-link

#include <type_traits>

namespace trying{

    struct to_do{};

    template <class...Checked, class T>
    std::enable_if_t <(std::is_same_v <T,Checked> || ...), std::true_type> 
    too_complex(T &&, to_do);

    template <class...Checked, class T>
    std::false_type 
    too_complex(T &&,...);
}

template <class U, class T, class...Checked>
concept Integer_= requires(const T& a, const T& b, const U& to_be_readable)
   {
   requires decltype(too_complex <T, Checked...> (a + b, to_be_readable))::value ;
   };

template <class T, class...Checked>
concept Integer = Integer_ <trying::to_do, T, Checked...>;

namespace trying{
    template <class...Checked, class T>
    requires (Integer <T, Checked...>)
    std::enable_if_t <!(std::is_same_v <T,Checked> || ...), std::true_type> 
    too_complex(T &&, to_do);
}

struct x{
    auto
    operator + (x) const -> int;
};
struct y{
    auto
    operator + (y) const -> void*;
};

struct z2;
struct z1{
    auto
    operator + (z1) const -> z2;
};
struct z2{
    auto
    operator + (z2) const -> z1;
};

static_assert (Integer <int>);
static_assert (Integer <x>);
static_assert (!Integer <y>);
static_assert (Integer <z1>);
static_assert (Integer <z2>);

是的,有可能...但是我不认为应该这样做。