add_const 不适用于转发引用

add_const won't work with forwarding references

我在将 add_const 类型特征应用于转发引用类型的情况下使用它。一切看起来都没有向类型添加常量,所以我做了一个小例子来验证情况是否如此(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&

类型特征按预期工作。您应该考虑您正在尝试做什么,即向引用添加常量。 您无法重新绑定引用(它不是可变的),因此基本上任何引用都是 const 引用

T& == T& const

我想你希望做的是创建一个对 const 类型的引用(这让我想起了 const iteratorconst_iterator 的事情),这是不能这样做的,因为同样的原因,你不能通过这种方式将 reference 类型定义为 对 const 类型 的引用:

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

综上所述,将 const 添加到引用类型使其成为 const 引用(与引用相同)而不是对 const 类型的引用

对于像这样的情况,具有将引用转移到新类型的类型特征会很有用。这可以补充对 constvolatile 执行相同操作的另一个特征,实现方式几乎相同。在你的例子中,如果你使用 T 而不是 decltype(arg),你 应该 只需要担心左值引用。但是,如果使用 lambda,您肯定也需要担心右值引用。

这是一个示例实现:

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 添加到引用类型,然后再将引用添加回来。