类型特征可以限制为不接受其他类型特征作为参数吗?

NoS*_*tAl 6 c++ type-traits c++-concepts c++20

问题可能很奇怪,所以这里是一个简短的激励示例:

#include <vector>
#include <type_traits>
template <typename T>
// workaround for gcc 8.3 where volatile int is not trivially copyable
using is_tc = std::is_trivially_copyable<std::remove_cv<T>>;
// static assert passes compile, oops
static_assert(is_tc<std::vector<int>>::value);
Run Code Online (Sandbox Code Playgroud)

如您所见,错误在于我将类型特征本身传递给另一个类型特征,而不是传递::type或使用std::remove_cv_t.

明显的解决方案是我不要犯错误,但我想知道 C++ 类型特征是否可以限制它们的输入类型,以便它们不接受其他 type_traits 作为参数。现在困难的是 type_traits 中有大量类型特征,所以 IDK 如何最好地实现这一点。

注意:我不是说 C++ 应该这样做,我知道要防止罕见的错误需要做很多工作,我只是想了解更复杂的概念设计,其中您的限制不基于类型的语义(又名具有 ++和 *) 但事实上类型属于一个庞大的类型集(并且该集包括您限制的类型)。

dfr*_*fri 2

std概念:是在命名空间中声明的TransformationTrait

我想知道是否有一种方法 C++ 类型特征可以限制它们的输入类型,以便它们不接受其他 type_traits

由于元函数特征实际上是类型本身(这也是问题的根源),我们可以利用这一点并构建一个概念,以确定T参数依赖查找 (ADL) 是否可以通过 ADL 找到较小的STL 函数选择集在类型的对象上T(在非评估上下文中),其中T可能是元功能特征;本质上是一种基于 ADL(可能很脆弱 - 见下文)的机制,用于查询给定类型是否T在命名空间中定义std,而不是查询是否T恰好是std命名空间中定义的众多特征类型之一的详细方法。

如果我们将其与TransformationTrait要求结合起来:

C++ 命名需求:TransformationTrait

TransformationTrait是一个类模板提供其模板类型参数的转换。

要求

  • 采用一个模板类型参数(其他模板参数是可选且允许的)
  • 转换后的类型是一个可公开访问的嵌套类型,名为type

我们可以为类型构造一个通用概念,T作为命名空间中的转换特征std。但请注意,如果由于某种原因给定项目开始声明重载 STL 中函数名称的函数,那么在这种意义上依赖 ADL 可能会有些脆弱。在可能的 ADL 查找概念中扩展较小的 STL 函数选择集将使非实现者的观点更难突破std

例如定义几个概念如下:

namespace traits_concepts {

template <typename T>
concept FindsStlFunctionByAdlLookupOnT = requires(T t) {
  // Rely on std::as_const being found by ADL on t, i.e.
  // for t being an object of a type in namespace std.
  as_const(t);

  // If we are worried that a user may define an as_const
  // function in another (reachable/found by lookup)
  // namespace, expand the requirement with additional
  // STL functions (that can be found via ADL).
  move(t);
  // ...

  // Remember to add the appropriate includes.
};

template <typename T>
concept IsTransformationTrait = requires {
  // REQ: The transformed type is a publicly accessible
  // nested type named type.
  typename T::type;
};

template <typename T>
concept IsStlTransformationTrait =
    IsTransformationTrait<T> && FindsStlFunctionByAdlLookupOnT<T>;

template <typename T>
concept IsNotStlTransformationTrait = !IsStlTransformationTrait<T>;

}  // namespace traits_concepts
Run Code Online (Sandbox Code Playgroud)

应用于:

namespace not_std {

template <traits_concepts::IsNotStlTransformationTrait T>
struct NotAnStlTrait {
  using type = T;
};

struct Foo {};

};  // namespace not_std

// Is an STL transformation trait
static_assert(
    traits_concepts::IsStlTransformationTrait<std::remove_cv<const int>>);
// Is not an STL transformation trait.
static_assert(
    !traits_concepts::IsStlTransformationTrait<std::remove_cv_t<const int>>);
static_assert(
    !traits_concepts::IsStlTransformationTrait<not_std::NotAnStlTrait<int>>);
static_assert(!traits_concepts::IsStlTransformationTrait<not_std::Foo>);

int main() {}
Run Code Online (Sandbox Code Playgroud)

并且,对于添加到的自定义特征std(假设现在我们是编译器供应商;将名称添加到std命名空间是 UB):

namespace std {

// Assume we are a compiler vendor.
// (Adding names to the std namespace is UB).
template <traits_concepts::IsNotStlTransformationTrait T>
struct custom_stl_trait {
  using type = T;
};

};  // namespace std

static_assert(
    traits_concepts::IsStlTransformationTrait<std::custom_stl_trait<int>>);
static_assert(!traits_concepts::IsStlTransformationTrait<
              std::custom_stl_trait<int>::type>);

int main() {}
Run Code Online (Sandbox Code Playgroud)

演示