add_const不适用于转发引用

时间:2016-02-10 13:15:18

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

我在add_const typetrait应用于转发引用类型的场景中使用它。看起来没有constness添加到类型中所以我做了一个小例子来验证是这种情况(PrintType是一个不完整的类型,会导致编译错误,迫使编译器吐出模板的名称错误消息中的参数):

#include <iostream>
#include <type_traits>
#include <complex>


template <class T>
struct PrintType; 

template <class T>
void f(T&& arg)
{
    PrintType<std::add_const_t<decltype(arg)>> local; 
    (void)local; 
}


int main() 
{
    std::complex<double> co; 
    f(co); 
}

错误消息says

main.cpp: In instantiation of 'void f(T&&) [with T = std::complex<double>&]':

main.cpp:20:9:   required from here

main.cpp:12:48: error: 'PrintType<std::complex<double>&> local' has incomplete type

     PrintType<std::add_const_t<decltype(arg)>> local; 

即特质将我的类型转换为T = std::complex<double>&而不是T = std::complex<double> const&

2 个答案:

答案 0 :(得分:3)

类型特征按预期工作。您应该考虑您尝试做什么,即将constness添加到引用。 你不能重新绑定引用(它不可变),所以基本上任何引用都是const引用

T& == T& const

我认为你期望做的是创建一个const类型的引用(这让我想起const iterator vs const_iterator thingy),这种方法不能这样做,原因相同你不能用这种方式将引用类型设置为对const类型的引用

typedef T& ref_t;
typedef const ref_t const_ref_t; // This is not T const& !! 

总而言之,将const添加到引用类型使其成为const引用(与引用相同)而不是对const类型的引用

答案 1 :(得分:1)

对于这样的情况,拥有一个将引用转移到新类型的类型特征会很有用。这可以补充对constvolatile执行相同操作的另一个特征,几乎完全相同。在您的情况下,如果您使用T而不是decltype(arg),则 只需要担心左值引用。但是,如果使用lambda,你肯定也需要担心rvalue引用。

以下是一个示例实现:

template<typename T, bool ApplyLvalueRef, bool ApplyRvalueRef>
struct apply_ref {
    static_assert(!(ApplyLvalueRef && ApplyRvalueRef), "Improper use: T cannot be & and &&");

    using possibly_lref = std::conditional_t<
        ApplyLvalueRef,
        std::add_lvalue_reference_t<T>,
        T
    >;

    using type = std::conditional_t<
        ApplyRvalueRef,
        std::add_rvalue_reference_t<possibly_lref>,
        possibly_lref
    >;
};

template<typename From, typename To>
struct transfer_ref : apply_ref<To, std::is_lvalue_reference<From>{}, std::is_rvalue_reference<From>{}> {};

template<typename From, typename To>
using transfer_ref_t = typename transfer_ref<From, To>::type;
乍一看,对于左值与右值相比,单独的布尔值似乎有点傻。但是,这允许不应用。永远不应该存在两者都是真的情况,这是由静态断言强制执行的。

现在我们可以轻松编写函数:

template <class T>
void f(T&& arg)
{
    using with_const = std::add_const_t<std::remove_reference_t<T>>;
    PrintType<transfer_ref_t<T, with_const>> local; 
    (void)local; 
}

由于我们无法将const应用于引用,因此我们必须将其删除,将const添加到引用类型,然后再添加引用。