类型特征是否可以限制为不接受其他类型特征作为参数?
Could type traits be restricted to not accept other type traits as arguments?
问题可能很奇怪,所以这里有一个简短的动机示例:
#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);
如您所见,错误是我将类型特征本身传递给了另一个类型特征,而不是传递 ::type
或使用 std::remove_cv_t
.
明显的解决方案是让我不犯错误,但我想知道是否有 C++ 类型特征可以限制它们的输入类型,以便它们不接受其他 type_traits 作为参数。
现在困难的是 type_traits 中有大量类型特征,因此 IDK 如何最好地实现它。
注意:我并不是说 C++ 应该这样做,我知道要防止罕见的错误需要做很多工作,我只是想了解更复杂的概念设计,其中您的限制不是基于类型的语义(又名有 ++ 和 *)但事实上类型属于一大组类型(并且该组包括您限制的类型)。
好吧,假设您总是尽可能需要一个 ::type
作为参数,这里有一个快速解决方法:
template<class T> concept HasType = requires { typename T::type; };
template<class T> concept HasNoType = !HasType<T>;
template<HasNoType T> using remove_cv = std::remove_cv<T>;
template<HasNoType T> using remove_cv_t = typename remove_cv<T>::type;
除了修补 STL headers 或子类化 STL 类型(这并不总是被允许)之外,您不能重新定义预定义的内容。
your restriction is not based on semantics of types(aka has ++ and *) but on the fact that types belong to a huge set of types
无论如何,您都需要一个谓词来指定此集合(运算符 ∊S
用于给定的 S)。例如 has ++
和其他谓词一样好。
可以使用更多级别的间接和一些样板来细化谓词,比如说
template<class T> struct not_a_type_trait =
std::integral_constant<bool, HasNoType<T>> {};
template<class T> inline constexpr not_a_type_trait_v = not_a_type_trait<T>::value;
template<class T> concept NotATrait = not_a_type_trait_v<T>;
struct AnArg { using type = void; };
template<> struct not_a_type_trait<AnArg>: std::true_type {};
// now can be an arg to remove_cv
或者,在这种特殊情况下,您可以简单地将所有 STL 的特征列入黑名单,但这将是一个非常庞大的谓词,要随每个标准修订版一起更新。
我认为如果所有特征都检查其他特征是可能的,比如所有特征都继承自 _Trait
,并在其模板参数上执行 is_base_of_v
:
template<class T>
struct remove_cv : private _Trait
{
static_assert(!is_base_of_v<_Trait, T>, "Don't pass traits to traits");
using type = T;
};
如果你想要警告而不是硬错误,这就更难了。需要使 static_assert
像往常一样评估 true
,但实例化 [[deprecated]]
class 以传递给 trait.
另一个简单的解决方案是标记 [[deprecated]]
所有需要 ::type
或 ::value
的特征,弃用它们以支持 _t
/ _v
。这是 non-standard,但可以在某些预处理器宏下完成。或者可以通过包含列出这些弃用的 header 来提供此弃用。
标记一组类型的常用方法是使用特征 ;-)
template <class T>
struct is_trait : std::false_type {};
template <class T>
struct is_trait <is_trait <T>> : std:: true_type {};
template <class T>
inline constexpr auto is_trait_v = is_trait:: value;
概念:是在 std
命名空间
中声明的 TransformationTrait
I wonder if is there a way C++ type traits could restrict their input types so that they do not accept other type_traits
由于元函数特征实际上是类型本身(这也是您问题的根源),我们可以利用这一点并为 T
构造一个概念,以确定参数依赖查找 (ADL) 是否可以找到更小的 select 集 的 STL 函数通过 ADL 在类型 T
的对象上(在非评估上下文中),其中 T
可能可能是元功能特征;本质上是一种基于 ADL(可能很脆弱 - 见下文)的机制来查询给定类型 T
是否在 std
命名空间中定义,而不是查询是否 [=14= 的冗长方法] 正是 std
命名空间中定义的众多特征类型之一。
如果我们将其与 TransformationTrait requirement 结合使用:
C++ named requirements: TransformationTrait
A TransformationTrait is a class template that provides a transformation of its template type parameter.
Requirements:
- Takes one template type parameter (additional template parameters are optional and allowed)
- The transformed type is a publicly accessible nested type named
type
我们可以为 T
类型构建一个通用概念,作为 std
命名空间中的转换特征。但是请注意,如果出于某种原因给定项目开始声明从 STL 重载函数名称的函数,那么在这种意义上依赖 ADL 可能会有些脆弱。在可能的 ADL 查找的概念中扩展 较小的 select 组 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
申请为:
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() {}
并且,对于添加到 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() {}
DEMO.
问题可能很奇怪,所以这里有一个简短的动机示例:
#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);
如您所见,错误是我将类型特征本身传递给了另一个类型特征,而不是传递 ::type
或使用 std::remove_cv_t
.
明显的解决方案是让我不犯错误,但我想知道是否有 C++ 类型特征可以限制它们的输入类型,以便它们不接受其他 type_traits 作为参数。 现在困难的是 type_traits 中有大量类型特征,因此 IDK 如何最好地实现它。
注意:我并不是说 C++ 应该这样做,我知道要防止罕见的错误需要做很多工作,我只是想了解更复杂的概念设计,其中您的限制不是基于类型的语义(又名有 ++ 和 *)但事实上类型属于一大组类型(并且该组包括您限制的类型)。
好吧,假设您总是尽可能需要一个 ::type
作为参数,这里有一个快速解决方法:
template<class T> concept HasType = requires { typename T::type; };
template<class T> concept HasNoType = !HasType<T>;
template<HasNoType T> using remove_cv = std::remove_cv<T>;
template<HasNoType T> using remove_cv_t = typename remove_cv<T>::type;
除了修补 STL headers 或子类化 STL 类型(这并不总是被允许)之外,您不能重新定义预定义的内容。
your restriction is not based on semantics of types(aka has ++ and *) but on the fact that types belong to a huge set of types
无论如何,您都需要一个谓词来指定此集合(运算符 ∊S
用于给定的 S)。例如 has ++
和其他谓词一样好。
可以使用更多级别的间接和一些样板来细化谓词,比如说
template<class T> struct not_a_type_trait =
std::integral_constant<bool, HasNoType<T>> {};
template<class T> inline constexpr not_a_type_trait_v = not_a_type_trait<T>::value;
template<class T> concept NotATrait = not_a_type_trait_v<T>;
struct AnArg { using type = void; };
template<> struct not_a_type_trait<AnArg>: std::true_type {};
// now can be an arg to remove_cv
或者,在这种特殊情况下,您可以简单地将所有 STL 的特征列入黑名单,但这将是一个非常庞大的谓词,要随每个标准修订版一起更新。
我认为如果所有特征都检查其他特征是可能的,比如所有特征都继承自 _Trait
,并在其模板参数上执行 is_base_of_v
:
template<class T>
struct remove_cv : private _Trait
{
static_assert(!is_base_of_v<_Trait, T>, "Don't pass traits to traits");
using type = T;
};
如果你想要警告而不是硬错误,这就更难了。需要使 static_assert
像往常一样评估 true
,但实例化 [[deprecated]]
class 以传递给 trait.
另一个简单的解决方案是标记 [[deprecated]]
所有需要 ::type
或 ::value
的特征,弃用它们以支持 _t
/ _v
。这是 non-standard,但可以在某些预处理器宏下完成。或者可以通过包含列出这些弃用的 header 来提供此弃用。
标记一组类型的常用方法是使用特征 ;-)
template <class T>
struct is_trait : std::false_type {};
template <class T>
struct is_trait <is_trait <T>> : std:: true_type {};
template <class T>
inline constexpr auto is_trait_v = is_trait:: value;
概念:是在 std
命名空间
中声明的 TransformationTrait
I wonder if is there a way C++ type traits could restrict their input types so that they do not accept other type_traits
由于元函数特征实际上是类型本身(这也是您问题的根源),我们可以利用这一点并为 T
构造一个概念,以确定参数依赖查找 (ADL) 是否可以找到更小的 select 集 的 STL 函数通过 ADL 在类型 T
的对象上(在非评估上下文中),其中 T
可能可能是元功能特征;本质上是一种基于 ADL(可能很脆弱 - 见下文)的机制来查询给定类型 T
是否在 std
命名空间中定义,而不是查询是否 [=14= 的冗长方法] 正是 std
命名空间中定义的众多特征类型之一。
如果我们将其与 TransformationTrait requirement 结合使用:
C++ named requirements: TransformationTrait
A TransformationTrait is a class template that provides a transformation of its template type parameter.
Requirements:
- Takes one template type parameter (additional template parameters are optional and allowed)
- The transformed type is a publicly accessible nested type named
type
我们可以为 T
类型构建一个通用概念,作为 std
命名空间中的转换特征。但是请注意,如果出于某种原因给定项目开始声明从 STL 重载函数名称的函数,那么在这种意义上依赖 ADL 可能会有些脆弱。在可能的 ADL 查找的概念中扩展 较小的 select 组 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
申请为:
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() {}
并且,对于添加到 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() {}
DEMO.