键入用于复制cv引用限定符的trait
Type trait for copying cv reference qualifiers
在C++中编写类似库的代码我发现copy_cv_reference_t
类型特征特别需要:
struct A;
struct B;
static_assert(std::is_same< copy_cv_reference_t< A , B >, B >{});
static_assert(std::is_same< copy_cv_reference_t< A const , B >, B const >{});
static_assert(std::is_same< copy_cv_reference_t< volatile A , B >, volatile B >{});
static_assert(std::is_same< copy_cv_reference_t< volatile A const , B >, volatile B const >{});
static_assert(std::is_same< copy_cv_reference_t< A &, B >, B & >{});
static_assert(std::is_same< copy_cv_reference_t< A const &, B >, B const & >{});
static_assert(std::is_same< copy_cv_reference_t< volatile A &, B >, volatile B & >{});
static_assert(std::is_same< copy_cv_reference_t< volatile A const &, B >, volatile B const & >{});
static_assert(std::is_same< copy_cv_reference_t< A &&, B >, B && >{});
static_assert(std::is_same< copy_cv_reference_t< A const &&, B >, B const && >{});
static_assert(std::is_same< copy_cv_reference_t< volatile A &&, B >, volatile B && >{});
static_assert(std::is_same< copy_cv_reference_t< volatile A const &&, B >, volatile B const && >{});
我使用两种方法为自己发明了它:通过类型限定符的id和仅通过SFINAE。
#include <type_traits>
#if 1
enum class type_qual_id
{
value,
const_value,
lref,
const_lref,
rref,
const_rref,
volatile_value,
volatile_const_value,
volatile_lref,
volatile_const_lref,
volatile_rref,
volatile_const_rref,
};
template< type_qual_id tqid, typename type > struct add_type_qualifier;
template< typename to > struct add_type_qualifier< type_qual_id::value , to > { using type = to ; };
template< typename to > struct add_type_qualifier< type_qual_id::const_value , to > { using type = to const ; };
template< typename to > struct add_type_qualifier< type_qual_id::lref , to > { using type = to & ; };
template< typename to > struct add_type_qualifier< type_qual_id::const_lref , to > { using type = to const & ; };
template< typename to > struct add_type_qualifier< type_qual_id::rref , to > { using type = to &&; };
template< typename to > struct add_type_qualifier< type_qual_id::const_rref , to > { using type = to const &&; };
template< typename to > struct add_type_qualifier< type_qual_id::volatile_value , to > { using type = volatile to ; };
template< typename to > struct add_type_qualifier< type_qual_id::volatile_const_value, to > { using type = volatile to const ; };
template< typename to > struct add_type_qualifier< type_qual_id::volatile_lref , to > { using type = volatile to & ; };
template< typename to > struct add_type_qualifier< type_qual_id::volatile_const_lref , to > { using type = volatile to const & ; };
template< typename to > struct add_type_qualifier< type_qual_id::volatile_rref , to > { using type = volatile to &&; };
template< typename to > struct add_type_qualifier< type_qual_id::volatile_const_rref , to > { using type = volatile to const &&; };
template< type_qual_id tqid, typename to >
using add_qualifier_t = typename add_type_qualifier< tqid, to >::type;
template< typename type > constexpr type_qual_id get_type_qualifier_id = type_qual_id::value ;
template< typename type > constexpr type_qual_id get_type_qualifier_id< type const > = type_qual_id::const_value ;
template< typename type > constexpr type_qual_id get_type_qualifier_id< type & > = type_qual_id::lref ;
template< typename type > constexpr type_qual_id get_type_qualifier_id< type const & > = type_qual_id::const_lref ;
template< typename type > constexpr type_qual_id get_type_qualifier_id< type && > = type_qual_id::rref ;
template< typename type > constexpr type_qual_id get_type_qualifier_id< type const && > = type_qual_id::const_rref ;
template< typename type > constexpr type_qual_id get_type_qualifier_id< volatile type > = type_qual_id::volatile_value ;
template< typename type > constexpr type_qual_id get_type_qualifier_id< volatile type const > = type_qual_id::volatile_const_value;
template< typename type > constexpr type_qual_id get_type_qualifier_id< volatile type & > = type_qual_id::volatile_lref ;
template< typename type > constexpr type_qual_id get_type_qualifier_id< volatile type const & > = type_qual_id::volatile_const_lref ;
template< typename type > constexpr type_qual_id get_type_qualifier_id< volatile type && > = type_qual_id::volatile_rref ;
template< typename type > constexpr type_qual_id get_type_qualifier_id< volatile type const && > = type_qual_id::volatile_const_rref ;
template< typename from, typename to >
using copy_cv_reference_t = add_qualifier_t< get_type_qualifier_id< from >, to >;
#else
#include <type_traits>
template< typename from, typename to >
struct copy_cv
{
using type = to;
};
template< typename from, typename to >
struct copy_cv< from const, to >
: copy_cv< from, to const >
{
};
template< typename from, typename to >
struct copy_cv< volatile from, to >
: copy_cv< from, volatile to >
{
};
template< typename from, typename to >
struct copy_cv< volatile from const, to >
: copy_cv< from, volatile to const >
{
};
template< typename from, typename to >
struct copy_reference
{
using type = to;
};
template< typename from, typename to >
struct copy_reference< from &, to >
: copy_reference< from, to & >
{
};
template< typename from, typename to >
struct copy_reference< from &&, to >
: copy_reference< from, to && >
{
};
template< typename from, typename to >
using copy_cv_reference_t = typename copy_reference< from, typename copy_cv< std::remove_reference_t< from >, to >::type >::type;
#endif
第一种方法看起来稍微有点人为,但提供了一个"类型限定符id"作为附加方,后者在某些情况下可能有用。第二种方法本质上是分两步走的。这可能有负面影响。此外,它还涉及std::remove_reference_t
来揭示cv合格类型。
一方面,我知道标准允许实现具有"内在"类型特征。另一方面,当前C++标准中没有类型特征。
copy_cv_reference_t
类型特性的最佳实现是什么?不仅介于两者之间。是否有更好的方法来实施它?是否有相应的建议?
命名怎么样?id的顺序如何?
我还没有遇到任何需要类似类型特征的用例,我也不知道有任何建议。因此,我只能提供一个更紧凑、IMHO更易于理解的实现:
template<typename T,typename U>
struct copy_cv_reference
{
private:
using R = std::remove_reference_t<T>;
using U1 = std::conditional_t<std::is_const<R>::value, std::add_const_t<U>, U>;
using U2 = std::conditional_t<std::is_volatile<R>::value, std::add_volatile_t<U1>, U1>;
using U3 = std::conditional_t<std::is_lvalue_reference<T>::value, std::add_lvalue_reference_t<U2>, U2>;
using U4 = std::conditional_t<std::is_rvalue_reference<T>::value, std::add_rvalue_reference_t<U3>, U3>;
public:
using type = U4;
};
template<typename T,typename U>
using copy_cv_reference_t = typename copy_cv_reference<T,U>::type;
实时示例
你是否觉得这是一种进步是主观的。
我建议您将特性/元函数分解为两部分。首先,它很好地分离了关注点:传播cv限定符和传播ref限定符这两项任务确实不同。我有时也会单独使用这两者。例如,指针qualifying_cv_of_t<A, B>*
不时出现,在这种情况下,我们绝对不希望指向引用的指针无效。(我的性状被命名为qualifying_*_of_t<A, B>
,可以理解为"A
的相关性质符合B
的相关性质"。)
其次,后一个特点是相当棘手。也许你想机械地复制顶级引用(如果有的话),在这种情况下就没有什么好说的了
[…]某种展开(例如,对于变量、可选、元组等)[…]
这无疑是我使用它的场景之一。我决定的一件事是,它实际上不是我关心的引用,而是价值类别。也就是说,qualifying_t<X, Y>
(传播一切的那个)在概念上表示decltype(expr.member)
†,其中expr
具有类型
struct X { Y member; };
可能简历合格。这种特性使写成为可能
template<typename T> qualifying_t<T, U> foo(T&& t)
{ return std::forward<T>(t).u; }
正确地(假设u
确实具有类型U
),即使例如U
是参考类型。那么,这有多棘手?好吧,就连标准委员会也还没有弄清楚C++14的所有细节→C++1z转换以修复C++11中引入的错误→C++14转换。我不会详细说明解决方案,因为我不认为一刀切:例如,std::tuple_element_t
和std::get
形成了一个非常相似的特征/功能模板对,它的作用与我上面概述的不同。
好的一面是,你可以根据需要写尽可能多的特质,并将其与你的qualifying_cv_of
相结合,你就可以继续了(事实上,我自己也有两个这样的特质!)。所以,也许真正的答案不是把特质一分为二,而是你需要多少。
†:敏锐的眼睛可能注意到了这里的一些东西,反而会认为是decltype( (expr.member) )
。至于哪一个更可取,或者为什么,我还没有一个令人满意的答案。
这里是一个boost::hana
风格的系统,用于限定符,而不是引用。
enum class qualifier:unsigned char {
none,
is_const = 1<<1,
is_volatile = 1<<2,
};
constexpr inline qualifier operator|(qualifier lhs,qualifier rhs){
return qualifier( unsigned(lhs)|unsigned(rhs) );
}
constexpr inline bool operator&(qualifier lhs,qualifier rhs){
return unsigned(lhs)&unsigned(rhs);
}
// not a simple alias to make operator overloading work right:
template<qualifier q>
struct qual_t:std::integral_constant<qualifier,q> {};
template<qualifier lhs, qualifier rhs>
constexpr qual_t<lhs|rhs> operator|(qual_t<lhs>,qual_t<rhs>){return {};}
template<class T>struct tag{using type=T;};
template<class Tag>using type_t=typename Tag::type;
template<class T>
constexpr qual_t<
(std::is_const<T>{}?qualifier::is_const:qualifier::none)
|(std::is_volatile<T>{}?qualifier::is_volatile:qualifier::none)
> qual(tag<T>={}){ return {}; }
template<class B, qualifier q,
class Step1=std::conditional_t<q&qualifier::is_const,const B,B>,
class R=std::conditional_t<q&qualifier::is_volatile,volatile Step1, Step1>
>
constexpr tag<R> add_qual( tag<B>={}, qual_t<q>={} ){ return {}; }
template<class T,qualifier Q>
auto operator+( tag<T> t, qual_t<Q> q ){
return add_qual(t,q);
}
template<class B, qualifier q>
using add_qual_t=type_t<decltype(tag<B>{}+qual_t<q>{})>;
使用以上内容,您可以使用tag<T>
类型或原始类型。
将参考资料从限定词中分离出来对我来说很有意义。
想看副本吗?
template<class From, class To>
constexpr auto copy_qual(tag<From> from={}, tag<To> to={}){
return to + qual(from);
}
可以转换为以下类型:
template<class From, class To>
using copy_qual_t=type_t<decltype(copy_qual<From,To>())>;
有点难看。
我们可以用参考做完全相同的事情
enum class ref_qualifier:unsigned char {
none,
rvalue,
lvalue
};
包括参考折叠
constexpr inline ref_qualfier operator|(ref_qualifier lhs, ref_qualifier rhs){
return ((unsigned)lhs>(unsigned)rhs)?lhs:rhs;
}
constexpr inline ref_qualfier operator&(ref_qualifier lhs, ref_qualifier rhs){
return ((unsigned)lhs>(unsigned)rhs)?rhs:lhs;
}
等等。(左值和右值限定符都以左值结尾)
我们可以写add_ref_qual
和sub_ref_qual
,用tag
s过载+
和-
。
template<class From, class To>
constexpr auto copy_ref_and_quals( tag<From> from, tag<To> to ) {
auto from_ref = ref_qual(from);
auto from_cv = qual(from-from_ref);
auto to_ref = ref_qual(to);
return (to-to_ref)+from_cv+to_ref+from_ref;
}
其中,我们将ref资格剥离到,然后添加from的cv资格,然后添加回from和to的ref资格。
以下是您的问题的即插即用解决方案:
#include<type_traits>
#include<cstddef>
static const std::size_t N = 42;
template<std::size_t N>
struct choice: choice<N-1> {};
template<>
struct choice<0> {};
template<typename T, typename U>
struct types {
using basic = T;
using decorated = U;
};
template<typename T, typename U>
auto f(choice<0>) { return types<T, U>{}; }
template<typename T, typename U, typename = std::enable_if_t<std::is_lvalue_reference<T>::value>>
auto f(choice<1>) {
auto t = f<std::remove_reference_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::add_lvalue_reference_t<D>>{};
}
template<typename T, typename U, typename = std::enable_if_t<std::is_rvalue_reference<T>::value>>
auto f(choice<2>) {
auto t = f<std::remove_reference_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::add_rvalue_reference_t<D>>{};
}
template<typename T, typename U, typename = std::enable_if_t<std::is_const<T>::value>>
auto f(choice<3>) {
auto t = f<std::remove_const_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::add_const_t<D>>{};
}
template<typename T, typename U, typename = std::enable_if_t<std::is_volatile<T>::value>>
auto f(choice<4>) {
auto t = f<std::remove_volatile_t<T>, U>(choice<N>{});
using B = typename decltype(t)::basic;
using D = typename decltype(t)::decorated;
return types<B, std::add_volatile_t<D>>{};
}
template<typename T, typename U>
auto f() {
return f<T, U>(choice<N>{});
}
template<typename T, typename U = char>
using copy_cv_reference_t = typename decltype(f<T, U>())::decorated;
struct A;
struct B;
int main() {
static_assert(std::is_same< copy_cv_reference_t< A , B >, B >{}, "!");
static_assert(std::is_same< copy_cv_reference_t< A const , B >, B const >{}, "!");
static_assert(std::is_same< copy_cv_reference_t< volatile A , B >, volatile B >{}, "!");
static_assert(std::is_same< copy_cv_reference_t< volatile A const , B >, volatile B const >{}, "!");
static_assert(std::is_same< copy_cv_reference_t< A &, B >, B & >{}, "!");
static_assert(std::is_same< copy_cv_reference_t< A const &, B >, B const & >{}, "!");
static_assert(std::is_same< copy_cv_reference_t< volatile A &, B >, volatile B & >{}, "!");
static_assert(std::is_same< copy_cv_reference_t< volatile A const &, B >, volatile B const & >{}, "!");
static_assert(std::is_same< copy_cv_reference_t< A &&, B >, B && >{}, "!");
static_assert(std::is_same< copy_cv_reference_t< A const &&, B >, B const && >{}, "!");
static_assert(std::is_same< copy_cv_reference_t< volatile A &&, B >, volatile B && >{}, "!");
static_assert(std::is_same< copy_cv_reference_t< volatile A const &&, B >, volatile B const && >{}, "!");
}
实现这一点的一种简洁方法是使用一个小助手实用程序,该实用程序基于某些条件应用元函数:
template <template <typename...> class MFn, bool condition, typename T>
using apply_if_t = std::conditional_t<condition, MFn<T>, T>;
这使我们能够组合不同的cvref限定符:
template <typename T>
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
template <typename From, typename To>
using copy_cv_t =
apply_if_t<std::add_volatile_t, std::is_volatile_v<From>,
apply_if_t<std::add_const_t, std::is_const_v<From>,
std::remove_cv_t<To>>>;
template <typename From, typename To>
using copy_ref_t =
apply_if_t<std::add_rvalue_reference_t, std::is_rvalue_reference_t<From>,
apply_if_t<std::add_lvalue_reference_t, std::is_lvalue_reference_t<From>,
std::remove_reference_t<To>>>;
template <typename From, typename To>
using copy_cvref_t = copy_ref_t<From,
copy_cv_t<std::remove_reference_t<From>, remove_cvref_t<To>>>;
- 将对象数组的引用传递给函数
- 什么时候在C++中返回常量引用是个好主意
- 我想将一个对T类型的非常量左值引用绑定到一个T类型的临时值
- 何时在引用或唯一指针上使用移动语义
- 如何在c++中使用引用实现类似python的行为
- 编译C++时未定义的引用
- Ctypes wstring通过引用传递
- c++r值引用应用于函数指针
- 理解c++中的引用
- C++取消引用指针.为什么会发生变化
- 如何修复此错误:未定义对"距离(浮点数,浮点数,浮点数,浮点数,浮点数)"的引用
- 我的项目不会像"undefined reference to `grpc::g_core_codegen_interface'"那样使用未定义的引用错误进行编译
- C++Boost Asio Pool线程,带有lambda函数和传递引用变量
- 强制转换为引用类型
- 引用一个已擦除类型(void*)的指针
- 向量元素的引用地址与它所指向的向量元素的地址不同.为什么
- 具有默认值的引用获取函数
- 如何使用基类指针引用派生类成员
- 使用取消引用的指针的多态性会产生意外的结果.为什么?
- 键入用于复制cv引用限定符的trait