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 iterator
与 const_iterator
的事情),这是不能这样做的,因为同样的原因,你不能通过这种方式将 reference 类型定义为 对 const 类型 的引用:
typedef T& ref_t;
typedef const ref_t const_ref_t; // This is not T const& !!
综上所述,将 const 添加到引用类型使其成为 const 引用(与引用相同)而不是对 const
类型的引用
对于像这样的情况,具有将引用转移到新类型的类型特征会很有用。这可以补充对 const
和 volatile
执行相同操作的另一个特征,实现方式几乎相同。在你的例子中,如果你使用 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
添加到引用类型,然后再将引用添加回来。
我在将 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 iterator
与 const_iterator
的事情),这是不能这样做的,因为同样的原因,你不能通过这种方式将 reference 类型定义为 对 const 类型 的引用:
typedef T& ref_t;
typedef const ref_t const_ref_t; // This is not T const& !!
综上所述,将 const 添加到引用类型使其成为 const 引用(与引用相同)而不是对 const
类型的引用
对于像这样的情况,具有将引用转移到新类型的类型特征会很有用。这可以补充对 const
和 volatile
执行相同操作的另一个特征,实现方式几乎相同。在你的例子中,如果你使用 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
添加到引用类型,然后再将引用添加回来。