自引用 c++20 概念
Self referential c++20 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
};
Suppose you want to check, as part of a concept, if some operation on a type results in a type that models such concept.
这就是无限递归。与任何函数递归一样,您必须有终止条件。为模板参数定义终止条件的正常方法是通过特化。但是 concept
s 明确地 不能 特化,所以不可能有终止条件。
这在逻辑上也是不连贯的,因为您试图通过使用您要定义的事物来编写定义。没有 "moral equivalent" 根据定义没有意义的东西。
您的概念似乎是在说“T
应该是我可以添加到另一个 T
并产生...”的东西?您是否希望它能够产生一些不相关的类型 U
可以将其添加到另一个 U
以产生...再次,什么?即使忽略那个问题,U
是否可以添加到 T
?如果是这样,那会产生什么?
写概念时,从用例开始,先确定要执行的操作。
可以进行这种递归模板检查,但会使代码难以阅读。
原理是将递归模板检查转发到通过依赖名称查找找到的函数,只有在类型不属于已检查类型列表时才会验证其约束......如果类型属于已经检查的列表checked类型,函数被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>);
所以是的,这是可能的...但我认为不应该这样做。
以下无效代码在道德上等同于什么?
// 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
};
Suppose you want to check, as part of a concept, if some operation on a type results in a type that models such concept.
这就是无限递归。与任何函数递归一样,您必须有终止条件。为模板参数定义终止条件的正常方法是通过特化。但是 concept
s 明确地 不能 特化,所以不可能有终止条件。
这在逻辑上也是不连贯的,因为您试图通过使用您要定义的事物来编写定义。没有 "moral equivalent" 根据定义没有意义的东西。
您的概念似乎是在说“T
应该是我可以添加到另一个 T
并产生...”的东西?您是否希望它能够产生一些不相关的类型 U
可以将其添加到另一个 U
以产生...再次,什么?即使忽略那个问题,U
是否可以添加到 T
?如果是这样,那会产生什么?
写概念时,从用例开始,先确定要执行的操作。
可以进行这种递归模板检查,但会使代码难以阅读。 原理是将递归模板检查转发到通过依赖名称查找找到的函数,只有在类型不属于已检查类型列表时才会验证其约束......如果类型属于已经检查的列表checked类型,函数被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>);
所以是的,这是可能的...但我认为不应该这样做。