通过弹出最后一个类型来创建一个新的元组类

时间:2014-12-01 14:07:00

标签: c++11 variadic-templates

我尝试了以下代码,但它给出了:

main.cpp:29:22:错误:聚合'pop<std::tuple<int, char, float> > p'类型不完整且无法定义

我错过了什么?

template <typename T>
struct pop;

template <typename E, typename... Ts>
struct pop<tuple<Ts..., E>> {
    using result = tuple<Ts...>;
};

tuple<int, char, float> t;
typename pop<decltype(t)>::result p;

如果Ts ...必须在类型列表的最后,那么为什么它在http://en.cppreference.com/w/cpp/language/parameter_pack的这个例子中起作用:

template<class A, class B, class...C> void func(A arg1, B arg2, C...arg3)
{
    container<A,B,C...> t1;  // expands to container<A,B,E1,E2,E3> 
    container<C...,A,B> t2;  // expands to container<E1,E2,E3,A,B> 
    container<A,C...,B> t3;  // expands to container<A,E1,E2,E3,B> 
}

4 个答案:

答案 0 :(得分:5)

tuple<Ts..., E>是一个非推断的上下文。 [temp.deduct.type] / 9:

  

如果P的表单包含<T><i>,那么相应模板参数列表{{}的每个参数P i 1}}与P的相应模板参数列表的相应参数A i 进行比较。 如果A的模板参数列表包含的包扩展不是最后一个模板参数,则整个模板参数列表是非推断的上下文。

这意味着您的部分专业化从不匹配。

使用C ++ 14,可以使用

P

这样

template <class T, class=std::make_index_sequence<std::tuple_size<T>::value-1>>
struct pop;

template <typename Tuple, std::size_t... indices>
struct pop<Tuple, std::index_sequence<indices...>>
{
    using type = std::tuple<std::tuple_element_t<indices, Tuple>...>;
};

template <typename T>
using pop_t = typename pop<T>::type;

编译。

Demo

答案 1 :(得分:3)

Ts...必须是类型列表的最后一个元素,如果您希望它被推导出来。 tuple<Ts...,E>不会推断Ts...只是最后一个,而是永远不会匹配。

摆脱最后一个参数有点过时了。 live example

#include <iostream>
#include <tuple>
#include <iostream>

namespace details {
  template<class Lhs, class Rhs>
  struct pop_helper;

  template<template<class...>class Tup, class L0, class...Lhs, class...Rhs>
  struct pop_helper<Tup<L0,Lhs...>, Tup<Rhs...>>:
    pop_helper<Tup<Lhs...>, Tup<Rhs...,L0>>
  {};
  template<template<class...>class Tup, class L0, class...Rhs>
  struct pop_helper<Tup<L0>, Tup<Rhs...>> {
    using type=Tup<Rhs...>;
  };
}

template <typename T>
struct pop;

template<template<class...>class Tup, class...Ts>
struct pop<Tup<Ts...>>:
  details::pop_helper<Tup<Ts...>,Tup<>>
{};

template<typename T>
using pop_t=typename pop<T>::type;

std::tuple<int, char, float> t;
typedef pop_t<decltype(t)> p;

int main() {
  p x = std::make_tuple( 7, 'x' );
  std::cout << std::get<0>(x) << std::get<1>(x) << std::tuple_size<p>{} << "\n";
}

pop_helper将类型一次一个地移动到右侧,直到左侧只剩下一种类型。然后它返回右侧类型。

pop只是传递了元组。

我使用template<class...>class Tup代替std::tuple,因为为什么不支持几乎每个template而不仅仅支持std::tuple

pop_t在使用时摆脱了恼人的typename垃圾邮件。

我使用inhertance-as-type-map-forwarding模式,这节省了打字。使用类型映射,结构:

template<class X>
struct bob: foo<X> {};

可以被理解为bob<X>foo<X>。另一种选择是更详细的

template<class X>
struct bob {
  using type = typename foo<X>::type;
};

扩展变量类型列表与匹配它们不同。在设计时,匹配保持简单,以使编译器供应商能够实现该功能。甚至可能还有棘手的问题&#34;它很棘手&#34;走那条路。

答案 2 :(得分:1)

另一种C ++ 11方法来修饰这只猫:

#include <tuple>

template<class Tuple>
struct pop;

template<>
struct pop<std::tuple<>>
{
    using type = std::tuple<>;
};

template<typename T1>
struct pop<std::tuple<T1>>
{
    using type = std::tuple<>;
};

template<typename First, typename... Rest>
struct pop<std::tuple<First,Rest...>>
{
    using type = 
    decltype(std::tuple_cat(
                std::declval<std::tuple<First>>(),
                std::declval<typename pop<std::tuple<Rest...>>::type>()));
};

// Test...

static_assert(std::is_same<pop<std::tuple<>>::type,std::tuple<>>::value,"");
static_assert(std::is_same<pop<std::tuple<int>>::type,std::tuple<>>::value,"");
static_assert(
    std::is_same<pop<std::tuple<int,char>>::type,std::tuple<int>>::value,"");
static_assert(
    std::is_same<pop<std::tuple<int,char,float>>::type,
        std::tuple<int,char>>::value,"");
static_assert(
    std::is_same<pop<std::tuple<int,char,float,double>>::type,
        std::tuple<int,char,float>>::value,"");

答案 3 :(得分:0)

这是我提出的解决方案:

template <typename T>
struct pop;

template <typename E, typename... Ts>
struct pop<std::tuple<E, Ts...>> {
    using type = decltype(tuple_cat(
        declval<tuple<E>>(),
        declval<typename pop<tuple<Ts...>>::type>()
    ));
};

template <typename E>
struct pop<std::tuple<E>> {
    using type = std::tuple<>;
};