问题描述
问题可能很奇怪,所以这里是一个简短的激励示例:
Question may be weird so here is a brief motivational example:
#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
.
As you can see mistake is that I have passed the type trait itself to another type trait instead of passing ::type
or using std::remove_cv_t
.
对我来说,显而易见的解决方案是不要犯错误,但是我想知道C ++类型特征是否可以限制它们的输入类型,以便它们不接受其他type_traits作为参数.现在的困难是,在type_traits中有大量的类型特征,因此IDK如何最好地实现这一点.
Obvious solution is for me to not make mistakes, but I wonder if is there a way C++ type traits could restrict their input types so that they do not accept other type_traits as arguments.Now the hard thing is that there is a huge set of type traits in type_traits so IDK how would one go best about implementing this.
注意:我并不是说C ++应该这样做,我知道要防止罕见的错误需要做很多工作,我只是想学习更复杂的概念设计,而您的约束并不基于类型的语义(aka具有++和*),但实际上类型属于大量类型(并且该类型包含您要限制的类型).
Note: I am not saying C++ should do this, I know it is a lot of work to prevent rare bugs, I am just trying to learn about more complicated concepts design where 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(and that set includes the type you are restricting on).
推荐答案
概念:是在 std
命名空间中声明的TransformationTrait
Concept: Is a TransformationTrait declared in the std
namespace
由于元功能特征本身实际上是类型(这也是问题的根源),因此我们可以利用它并为 T
构造一个概念,以确定参数依赖查找(ADL)是否可以在类型为 T
的对象上(通过未评估的上下文),通过ADL查找STL函数的较小选择集.可能是元功能特征;本质上是一种基于ADL的机制(可能很脆弱-参见下文),用于查询是否在 std
命名空间中定义了给定类型的 T
,这与冗长的方法相反.查询 T
是否正好是 std
命名空间中定义的众多特征类型之一.
Since metafunction traits are actually types themselves (which is kind of also the root of your problem), we can leverage this and construct a concept for T
for whether Argument-Dependent Lookup (ADL) can find a smaller select set of STL-functions via ADL on an object of type T
(in a non-evaluated context), where T
may potentially be a metafunction trait; essentially an ADL-based (possibly brittle - see below) mechanism to query whether a given type T
is defined in the std
namespace or not, as opposed to the verbose approach of querying whether T
is exactly one of numerous trait types defined in the std
namespace.
如果我们将其与 TransformationTrait 要求结合起来:
If we combine this with the TransformationTrait requirement:
要求:
- 采用一个模板类型参数(其他模板参数是可选的,并且允许)
- 转换后的类型是一个可公开访问的嵌套类型,名为
type
我们可以为类型 T
构建通用概念,该类型是 std
命名空间中的转换特征.但是请注意,如果给定的项目出于某种原因开始声明使STL中的函数名超载的函数,则从某种意义上说依赖ADL可能会有些脆弱.在概念中扩展 STL函数的较小选择集以进行可能的ADL查找,将使从非 std -实现者的角度来看更难以突破.
we can construct a common concept for a type T
being a transformation trait in the std
namespace. Note however that relying on ADL in this sense can be somewhat brittle, if for some reason a given project starts to declare functions which overloads function names from the STL. Expanding the smaller select set of STL-functions in the concept for possible ADL-lookup will make it harder to break from a non-std
-implementor perspective.
例如定义了一些概念,如下所示:
E.g. defining a few concepts as follows:
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):
And, for a custom trait added to std
(assume for now that we are a compiler vendor; adding names to the std
namespace is 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() {}
演示 .
这篇关于是否可以将类型特征限制为不接受其他类型特征作为参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!