c++ - 获得通用引用的优势,无需通用引用

标签 c++ templates c++11 universal-reference

问题

让我们假设一个函数 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

一种选择是简单地使 ContainerN 存储在 static 变量中(或在 typedef' 中d std::integral_constantconstexpr 函数)。另一种选择是编写一个新的(元)函数,其唯一目的是查找 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/

相关文章:

c++ - 为什么继承值打印了两次?

c++ - 获取模板基类的类型

templates - 带有模板参数的 C++0x lambdas?

c++ - 如何为引用参数定义模板函数和为指针参数定义相同的函数

c++ - 在各种开关情况下初始化变量

c++ - 依赖于模板参数的成员函数实现

c++ - CMake 和 wxWidgets 2.9.5

c++ - 如何在 C++ 中将十六进制转换为 IEEE 754 32 位 float

php - 设计模式问题: encapsulation or inheritance

c++ - 无法创建 constexpr std::vector