c++ 获得通用参考的优点,无需通用参考

问题

让我们假设一个函数func,它采用Container< Type,N,Args ...>的形式的任何容器. (这是一个容器,它将第一个模板参数作为一个类型,第二个是std :: size_t定义容器中有多少个参数),并且当且仅当N在40和42之间时才返回其第i个元素.

这样一个容器的例子是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];
}

为了定义该函数的单一版本,并且对于Container< Type,N,Args ...&和const Container< Type,N,Args ...&

最佳答案
您不能在没有实际使用通用引用的情况下获得“通用引用”的优点,因此只需将Container作为“通用引用”参数即可.如果你这样做,你需要做的就是使用替代技术来找到N.

一个选择是将容器存储N简单地放在一个静态变量(或typedef’d std :: integral_constant或一个constexpr函数)中.另一个选择是写一个新的(meta-)函数,其唯一目的是找到N.我更喜欢第一个选项,但是我将在答案中写出第二个选项,因为它不那么干涉(它不是’ t需要对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];
}

现在,您需要使用容器的cv-ness和value类别转发container [i]的功能.为此,使用一个辅助函数,这是一个泛化的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++ 获得通用参考的优点,无需通用参考 - 代码日志