2014-12-01 20 views
8

Próbowałem poniższy kod, ale daje:Tworzenie nowej klasy krotka popping ostatni typ

main.cpp: 29: 22: error: agregat 'pop<std::tuple<int, char, float> > p' ma niekompletną typu i nie mogą być definiowane

Co czy ja tęsknię?

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; 

Jeśli Ts ... musi być w końcu na liście typu, dlaczego to działa w tym przykładzie z 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> 
} 
+3

Watykan chce mieć słowo z tobą ... (tytuł edytowany) – sehe

Odpowiedz

5

tuple<Ts..., E> jest kontekstem niewydechem. [Temp.deduct.type]/9:

If P has a form that contains <T> or <i> , then each argument Pi of the respective template argument list P is compared with the corresponding argument Ai of the corresponding template argument list of A . If the template argument list of P contains a pack expansion that is not the last template argument, the entire template argument list is a non-deduced context.

Oznacza to, że częściowa specjalizacja jest nigdy dopasowane.

z C++ 14, można zastosować

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; 

taki sposób, że

using t = std::tuple<int, char, float>; 
static_assert(std::is_same<pop_t<t>, std::tuple<int, char>>{}, ""); 

zestawia.

Demo.

+0

Świetna odpowiedź ... Jak rozwiązać problem za pomocą C++ 11? –

+0

@bmm Zasadniczo musisz użyć 'typename std :: tuple_element :: type' jako wzorca ekspansji i napisać własną' make_index_sequence' (która jest parą linii). Jeśli chcesz, zintegruję to z odpowiedzią. – Columbo

+0

Rozumiem. Nie, dziękuję, to będzie dla mnie ćwiczenie :) –

3

Ts... musi być ostatnim elementem listy typu, jeżeli chcesz to wydedukować. tuple<Ts...,E> nie wywnioskuje, że Ts... jest wszystkim, ale ostatnim, ale nigdy nie pasuje do niczego.

Pozbycie się ostatniego argumentu jest nieco tricker. 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 porusza typy ponad jednej na raz z prawej strony, dopóki istnieje tylko jeden rodzaj lewo na lewej stronie. Następnie zwraca typ prawej strony.

właśnie przechodzi krotki.

Użyłem template<class...>class Tup zamiast std::tuple, ponieważ dlaczego nie obsługiwać prawie każdego template zamiast tylko std::tuple?

pop_t pozbywa się irytującego spamu w miejscu użytkowania.

Używam wzoru przekazywania typu inhertance-as-type, który zapisuje się podczas pisania. Z typu mapy, struktury:

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

można odczytać jako bob<X> jest foo<X>. Alternatywą jest bardziej szczegółowe poszerzanie list typów odmian różni się od ich dopasowywania. Po zaprojektowaniu dopasowanie było proste, aby dostawcy kompilatorów mogli zaimplementować tę funkcję. Mogą pojawić się nawet drażliwe problemy wykraczające poza "trudną" ścieżkę.

+0

Wow, to jedno sprytne rozwiązanie !! –

0

Jest to rozwiązanie I wymyślił:

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<>; 
}; 
+3

Powoduje to utworzenie 'tuple >' z 'tuple ', co do którego wątpię, jest tym co chcesz. Możesz wypróbować rozwiązanie oparte na 'decltype (tuple_cat'?) – Yakk

+0

Dla jasności, myślę, że rozwiązuje to błąd w powyższym:' template struct pop; template struct pop > {używając type = std :: tuple_cat_t , nazwa pliku pop > :: type>;}; template struct pop > {używając type = std :: tuple <> ;};; ' – Yakk

+0

@Yakk Dzięki, poprawiłem odpowiedź. | Myślę, że odpowiedź nie zasługuje na nic więcej. –

1

Innym C++ 11 sposób na skórę ten kot:

#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,""); 
+0

Czy 'pop >' specjalizacja nie jest objęta 'pop >'? –

+0

Masz oczywiście rację! Usunąłem ten. –