问题
让我们假设一个函数 func
采用 Container<Type, N, Args...>
形式的任何容器(这是一个容器,第一个模板参数是一个类型,第二个是 std::size_t
,定义容器中有多少个参数)并返回它的 i
th 元素当且仅当 N
介于 40
之间和 42
.
这种容器的一个例子是 std::array
.
我的第一个版本的函数是这样的:
template
< template<class, std::size_t, class...> class Container
, class Type
, std::size_t N
, class... Args >
auto func(std::size_t i, Container<Type, N, Args...>& container) -> decltype(container[0]) {
static_assert(N >= 40 && N <= 42, "bla bla bla");
return container[i];
}
然后我需要一个 const
过载:
template
< template<class, std::size_t, class...> class Container
, class Type
, std::size_t N
, class... Args >
auto func(std::size_t i, const Container<Type, N, Args...>& container) -> decltype(container[0]) {
static_assert(N >= 40 && N <= 42, "bla bla bla");
return container[i];
}
问题
是否可以定义类似的东西(这不起作用,因为这不是通用引用):
template
< template<class, std::size_t, class...> class Container
, class Type
, std::size_t N
, class... Args >
auto func(std::size_t i, Container<Type, N, Args...>&& container) -> decltype(container[0]) {
// ^^
static_assert(N >= 40 && N <= 42, "bla bla bla");
return container[i];
}
为了定义这个函数的单一版本,make 对 Container<Type, N, Args...>&
都有效。和 const Container<Type, N, Args...>&
?
最佳答案
如果不实际使用通用引用,则无法获得“通用引用”的优势,因此只需将 Container
设为“通用引用”参数。如果您这样做,您需要做的就是使用另一种技术来查找 N
。
一种选择是简单地使 Container
将 N
存储在 static
变量中(或在 typedef
' 中d std::integral_constant
或 constexpr
函数)。另一种选择是编写一个新的(元)函数,其唯一目的是查找 N
。我更喜欢第一个选项,但我会在答案中写第二个选项,因为它的侵入性较小(它不需要对 Container
进行任何更改)。
//This can alternatively be written as a trait struct.
template
< template<class, std::size_t, class...> class Container
, class Type
, std::size_t N
, class... Args >
constexpr std::size_t get_N(Container<Type, N, Args...> const&) { return N; }
template <class Container>
auto func(std::size_t i, Container &&container) -> decltype(container[i]) {
//alternatively, use std::tuple_size or container.size() or whatever
constexpr std::size_t N = get_N(container);
static_assert(N >= 40 && N <= 42, "bla bla bla");
return container[i];
}
现在您需要能够将 container[i]
与 container
的 cv-ness 和 value 类别一起转发。为此,请使用 std::forward
泛化的辅助函数。这真的很难看,因为标准库中对此没有太多支持(幸运的是,您只需要编写一次,它对于很多不同的问题都很有用)。首先是类型计算:
template<typename Prototype, typename T_value, typename T_decayed>
using forward_Const_t =
typename std::conditional<
std::is_const<Prototype>::value || std::is_const<T_value>::value,
T_decayed const,
T_decayed
>::type;
template<typename Prototype, typename T_value, typename T_decayed>
using forward_CV_t =
typename std::conditional<
std::is_volatile<Prototype>::value || std::is_volatile<T_value>::value,
forward_Const_t<Prototype, T_value, T_decayed> volatile,
forward_Const_t<Prototype, T_value, T_decayed>
>::type;
template<typename Prototype, typename T>
struct forward_asT {
static_assert(
std::is_reference<Prototype>::value,
"When forwarding, we only want to be casting, not creating new objects.");
static_assert(
!(std::is_lvalue_reference<Prototype>::value &&
std::is_rvalue_reference<T>::value),
"Casting an rvalue into an lvalue reference is dangerous");
typedef typename std::remove_reference<Prototype>::type Prototype_value_t;
typedef typename std::decay<T>::type T_decayed;
typedef typename std::remove_reference<T>::type T_value;
typedef typename std::conditional<
std::is_lvalue_reference<Prototype>::value,
forward_CV_t<Prototype_value_t, T_value, T_decayed> &,
forward_CV_t<Prototype_value_t, T_value, T_decayed> &&>::type type;
};
template<typename Prototype, typename T>
using forward_asT_t = typename forward_asT<Prototype,T>::type;
现在的功能:
//Forwards `val` with the cv qualification and value category of `Prototype`
template<typename Prototype, typename T>
constexpr auto forward_as(T &&val) -> forward_asT_t<Prototype, T &&> {
return static_cast<forward_asT_t<Prototype, T &&>>(val);
}
现在已经定义了辅助函数,我们可以简单地将func
写成:
template <typename Container>
auto func(std::size_t i, Container &&container) ->
decltype(forward_as<Container &&>(container[i]))
{
constexpr std::size_t N = get_N(container);
static_assert(N >= 40 && N <= 42, "bla bla bla");
return forward_as<Container &&>(container[i]);
}
关于c++ - 获得通用引用的优势,无需通用引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24973164/