键入trait以复制cv引用限定符

时间:2015-07-01 21:06:15

标签: c++ c++11 c++14 typetraits c++17

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

第一种方法看起来稍微有些人为,但提供了一种&#34;类型限定符id&#34;因为额外的一方和后一方在某些情况下可能有用。第二种方法本质上是两步法。它可能有缺点。此外,它涉及std::remove_reference_t以揭示cv限定类型。

一方面,我知道标准允许实现具有"intrinsic" type traits。另一方面,目前 C ++ 标准中没有类型特征。

copy_cv_reference_t类型特征的最佳实现是什么?不仅在两个之间。是否有更好的方法来实现它?是否有相应的提案?

命名怎么样? ids的顺序怎么样?

5 个答案:

答案 0 :(得分:5)

我没有遇到任何需要这种类型特征的用例,我不知道任何提议。因此,我只能提供更紧凑的实现,恕我直言更容易理解:

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;

Live example

你是否认为这是一种改进是主观的。

答案 1 :(得分:3)

这是一个boost::hana esque系统,用于限定符,而不是引用。

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> opetator|(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

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 qualidence中加回。

答案 2 :(得分:2)

我建议你将你的特质/元功能分解为两个。首先,它是关注点的良好分离:传播cv限定符和传播ref-qualifiers的两个任务确实是不同的。我有时也会孤立地使用这两者。例如。指针qualifying_cv_of_t<A, B>*不时出现,在这种情况下,我们绝对不希望指向引用,因为它们是无效的。 (我的特征名为qualifying_*_of_t<A, B>,可以理解为A的相关属性符合B&#39;的条件。)

其次,后者的特质是相当棘手才能正确。也许你想机械地复制顶级参考(如果存在),在这种情况下,没有太多可说的。另一方面,你说:

  

[...]某种展开(例如,对于变体,可选,元组等)[...]

这绝对是我使用它的场景之一。我已经决定的一件事是,它实际上不是我关心的参考,它是价值类别。也就是说,qualifying_t<X, Y>(传播所有内容的那个)在概念上代表decltype(expr.member)†其中expr具有类型

struct X { Y member; };

可能是cv合格的。该特征使得编写例如

成为可能
template<typename T> qualifying_t<T, U> foo(T&& t)
{ return std::forward<T>(t).u; }

正确(假设u确实有类型U),即使例如U是引用类型。那么,这有多棘手?好吧,即使the Standard Committee还没有弄清楚C ++ 14→C ++ 1z过渡的所有细节,以修复C ++ 11→C ++ 14过渡中引入的错误。我不会说出一个解决方案,因为我不相信一种尺寸适合所有:例如std::tuple_element_tstd::get构成一个非常相似的特征/功能模板对,它与我上面概述的内容有所不同。

好处是你可以根据自己的需要写出多少特质,将它与你的qualifying_cv_of结合起来,你很高兴(事实上,我自己有两个这样的特质!)。所以也许真正的答案不是把这个特性分成两部分,而是你所需要的很多。

†:敏锐的目光可能已经注意到了这里的一些东西,而是会假设decltype( (expr.member) )之类的东西。我还没有一个令人满意的答案,哪个更好,或者为什么。

答案 3 :(得分:1)

以下是解决问题的即插即用解决方案:

#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 && >{}, "!");
}

答案 4 :(得分:0)

一种简单的实现方法是使用一个小助手实用程序,该实用程序根据某些条件应用元函数:

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>>>;