Частичная специализация с переменными параметрами шаблона, за которыми следуют другие параметры - PullRequest
0 голосов
/ 16 февраля 2019

У меня проблема с частичной специализацией, связанной с параметрами шаблона.Специализация с префиксом

template<typename A, typename ... B>
struct Foo<A, B...> { };

работает как положено, но когда я пытаюсь сопоставить его с постфиксом

template<typename A, typename ... B>
struct Foo<B..., A> { };

, это почему-то не работает.Есть ли какое-то правило, о котором я не знаю, или это проблема компилятора?(Я использую G ++ 7.4, а именно x86_64-w64-mingw32-g ++ от cygwin)

Автономный пример, демонстрирующий мою проблему:

#include <iostream>

template<char ... C>
struct Str
{
    static constexpr char Value[] = { C..., '\0' };
};

template<char ... C>
constexpr char Str<C...>::Value[];

template<typename>
struct TrimFront;

template<char A, char ... C>
struct TrimFront<Str<A, C...>>
{
    typedef Str<C...> Type;
};

template<typename>
struct TrimBack;

template<char A, char ... C>
struct TrimBack<Str<C..., A>>
{
    typedef Str<C...> Type;
};

int main(int, char **)
{
    typedef Str<'a', 'b', 'c', 'd', 'e', 'f'> str_t;
    std::cout << str_t::Value << std::endl; // prints "abcdef"
    std::cout << TrimFront<str_t>::Type::Value << std::endl; // prints "bcdef"
    std::cout << TrimBack<str_t>::Type::Value << std::endl; // ERROR (incomplete type)
    return 0;
}

Ответы [ 3 ]

0 голосов
/ 17 февраля 2019

Я полагаю, что

template<char A, char ... C>
struct TrimBack<Str<C..., A>>
{
    typedef Str<C...> Type;
};

не может работать ("A" и "C...") не может быть выведено, потому что пакет с переменным числом (C...) не в последней позиции.

ОП, разумно, попросить ссылку

на самом деле?очень неудачноМожете ли вы случайно указать, где это сказано в стандарте?Кажется, я не могу найти соответствующую часть

Я не являюсь языковым слоем, но мне кажется, что соответствующая часть (стандарт C ++ 11) - 14.8.2.5 («Вывод аргумента шаблона из типа"," [temp.deduct.type] "), точка 9 (выделено мной)

Если P имеет форму, содержащую <T> или <i>, то каждый аргумент P_i соответствующего списка аргументов шаблона P сравнивается с соответствующим аргументом A_i соответствующего списка аргументов шаблона A. Если список аргументов шаблона в P содержит расширение пакета, которое не является последним аргументом шаблона, весь список аргументов шаблона представляет собой не выводимый контекст .Если P_i является расширением пакета, то шаблон P_i сравнивается с каждым оставшимся аргументом в списке аргументов шаблона A.Каждое сравнение выводит аргументы шаблона для последующих позиций в пакетах параметров шаблона, расширенных на P_i.

Итак, если я не ошибаюсь, TrimBack<str_t> (он же TrimBack<Str<'a', 'b', 'c', 'd', 'e', 'f'>>) выдает ошибку, потому что

1) на первом этапе, Str<C..., A> соответствует Str<'a', 'b', 'c', 'd', 'e', 'f'>

2) но на втором этапе, пытаясь вывести типы C... и A, P (то есть Str<C..., A>, на этом этапе) "содержит расширение пакета, которое не является последнимАргумент шаблона ", поэтому" весь список аргументов шаблона представляет собой недоопределенный контекст ".

0 голосов
/ 17 февраля 2019

Частичная специализация шаблона класса, подобная этой

template<typename> struct TrimBack;
template<char ...C, char A> struct TrimBack<Str<C..., A>> {}

, не допускается, поскольку для вывода C... и A, выполняется вычет из типа и пакетаргумент, который не является последним, делает его невыгруженным контекстом .

Вместо этого вы можете использовать вспомогательный тип, чтобы «развернуть» пакет, а затем «переупаковать» его,меньше последнего элемента.

template <char ...P>
struct dummy {};

template <class T, char ...P>
struct internal;

template <char ...P1, char T, char ...P2>
struct internal<dummy<P1...>, T, P2...>
{
    using type = typename internal<dummy<P1..., T>, P2...>::type; // unwrap one recursively
};

template <char ...P1, char T>
struct internal<dummy<P1...>, T>
{
    using type = Str<P1...>; // re-wrap all but the last one
};

template <typename>
struct TrimBack;

template <char ...C>
struct TrimBack<Str<C...>>
{
    using Type = typename internal<dummy<>, C...>::type;
};

Теперь это должно работать:

std::cout << TrimBack<str_t>::Type::Value << std::endl;  // prints "abcde"

Демонстрационная версия

0 голосов
/ 16 февраля 2019

Вот решение с использованием boost::mp11:

Встроенные комментарии:

#include <iostream>
#include <boost/mp11.hpp>

template<char ... C>
struct Str
{
    static constexpr char Value[] = { C..., '\0' };
};

template<char ... C>
constexpr char Str<C...>::Value[];

template<typename>
struct TrimFront;

template<char A, char ... C>
struct TrimFront<Str<A, C...>>
{
    typedef Str<C...> Type;
};

template<typename>
struct TrimBack;



using namespace boost::mp11;

// a means of turning chars into types
template<char c> struct c_char {
    constexpr char value() { return c; }
 };

// a means of turning an mp_list of c_char<char>... back into a Str<char...>
 template<typename>
 struct back_to_Str;

 template<char...cs>
 struct back_to_Str<mp_list<c_char<cs>...>>
 {
     using result = Str<cs...>;
 };

// TrimBack using types as computation steps:
template<char... C>
struct TrimBack<Str<C...>>
{ 
    // turn the input chars into an mp_list of c_char
    // always use types, they're much easier than values when metaprogramming
    using input = mp_list<c_char<C>...>;        

    // reverse the list
    using reversed = mp_reverse<input>;

    // pop the front c_char<>
    using popped = mp_pop_front<reversed>;

    // reverse again
    using re_reversed = mp_reverse<popped>;

    // turn back into a Str<char...>
    using Type = typename back_to_Str<re_reversed>::result;
};

int main(int, char **)
{
    typedef Str<'a', 'b', 'c', 'd', 'e', 'f'> str_t;
    std::cout << str_t::Value << std::endl; // prints "abcdef"
    std::cout << TrimFront<str_t>::Type::Value << std::endl; // prints "bcdef"
    std::cout << TrimBack<str_t>::Type::Value << std::endl; // prints "abcde"
    return 0;
}

Ожидаемый результат:

abcdef
bcdef
abcde

http://coliru.stacked - криво.com / a / 387e5dc7ef262f1f

Благодаря нашим новым знаниям мы можем упростить:

#include <iostream>
#include <boost/mp11.hpp>

using namespace boost::mp11;

template<char c> 
struct c_char {
    static constexpr char value() { return c; }
};

template<typename...> struct Str;

template<char... C>
struct Str<c_char<C>...>
{
    static constexpr auto size() -> std::size_t { return sizeof...(C) + 1; }
    static constexpr char Value [size()] = { C..., '\0' };
};

template<char...C> using make_Str = Str<c_char<C>...>;

template<typename List>
struct TrimFront
{
    using Type = mp_pop_front<List>;
};

template<typename List>
struct TrimBack
{ 
    using Type = mp_reverse<mp_pop_front<mp_reverse<List>>>;
};

int main(int, char **)
{
    using str_t = make_Str<'a', 'b', 'c', 'd', 'e', 'f'>;
    std::cout << str_t::Value << std::endl; // prints "abcdef"
    std::cout << TrimFront<str_t>::Type::Value << std::endl; // prints "bcdef"
    std::cout << TrimBack<str_t>::Type::Value << std::endl; // prints "abcde"
    return 0;
}
...