Подтвердить что ты не робот

Ошибка GCC с вариативными шаблонами: "Извините, не реализовано: не удается развернуть" Идентификатор... "в список аргументов фиксированной длины"

При выполнении вариационного программирования шаблонов в С++ 11 в GCC время от времени появляется сообщение об ошибке "Извините, не реализовано: невозможно развернуть" Идентификатор... "в список исправлений фиксированной длины". Если я удалю "..." в коде, тогда получаю другую ошибку: "error: пакеты параметров не расширены с помощью "..." .

Итак, если у меня есть "..." , GCC вызывает эту ошибку, и если я возьму "..." , GCC вызовет также и ошибку.

Единственный способ, с которым я смог справиться, - полностью переписать метапрограмму шаблона с нуля с использованием другого подхода, и (с удачей) я в конечном итоге придумал код, который не вызывает ошибку. Но я действительно хотел бы знать, что я делаю неправильно. Несмотря на то, что для него и для Google, и несмотря на много экспериментов, я не могу определить, что я делаю по-разному между вариационным кодом шаблона, который вызывает эту ошибку, и кодом, который не имеет ошибки.

Формулировка сообщения об ошибке означает, что код должен работать в соответствии со стандартом С++ 11, но GCC еще не поддерживает его. Или, возможно, это ошибка компилятора?

Вот код, который вызывает ошибку. Примечание. Мне не нужно писать для меня правильную реализацию, а просто указать, что такое мой код, вызывающий эту конкретную ошибку.

// Used as a container for a set of types.
template <typename... Types> struct TypePack
{
    // Given a TypePack<T1, T2, T3> and T=T4, returns TypePack<T1, T2, T3, T4>
    template <typename T>
    struct Add
    {
        typedef TypePack<Types..., T> type;
    };
};

// Takes the set (First, Others...) and, while N > 0, adds (First) to TPack.
// TPack is a TypePack containing between 0 and N-1 types.
template <int N, typename TPack, typename First, typename... Others>
struct TypePackFirstN
{
    // sorry, unimplemented: cannot expand ‘Others ...’ into a fixed-length argument list
    typedef typename TypePackFirstN<N-1, typename TPack::template Add<First>::type, Others...>::type type;
};

// The stop condition for TypePackFirstN:  when N is 0, return the TypePack that has been built up.
template <typename TPack, typename... Others>
struct TypePackFirstN<0, TPack, Others...> //sorry, unimplemented: cannot expand ‘Others ...’ into a fixed-length argument list
{
    typedef TPack type;
};

РЕДАКТИРОВАТЬ: Я заметил, что, хотя похожее на экземпляр частичного экземпляра вызывает ошибку:

template <typename... T>
struct SomeStruct<1, 2, 3, T...> {};

Переписывание, поскольку это не вызывает ошибки:

template <typename... T>
struct SomeStruct<1, 2, 3, TypePack<T...>> {};

Кажется, что вы можете объявлять параметры для частичных специализаций вариационными; то есть эта строка в порядке:

template <typename... T>

Но вы не можете использовать эти пакеты параметров в специализации, то есть эта часть не подходит:

SomeStruct<1, 2, 3, T...>

Тот факт, что вы можете заставить его работать, если вы завернете пакет в какой-то другой тип, например:

SomeStruct<1, 2, 3, TypePack<T...>>

для меня подразумевается, что объявление вариационного параметра для специализации частичного шаблона было успешным, и вы просто не можете использовать его напрямую. Кто-нибудь может это подтвердить?

4b9b3361

Ответ 1

Есть трюк, чтобы заставить это работать с gcc. Эта функция еще не реализована полностью, но вы можете структурировать код, чтобы избежать нереализованных разделов. Ручное расширение вариационного шаблона в список параметров не будет работать. Но специализация шаблонов может сделать это для вас.

template< char head, char ... rest >
struct head_broken
{
   static const char value = head;
};

template< char ... all >
struct head_works; // make the compiler hapy

template< char head, char ... rest >
struct head_works<head,rest...> // specialization
{
   static const char value = head;
};

template<char ... all >
struct do_head
{
   static const char head = head_works<all...>::value;
   //Sorry, unimplemented: cannot expand 'all...' into a fixed-length arugment list
   //static const char head = head_broken<all...>::value;
};

int main
{
   std::cout << head_works<'a','b','c','d'>::value << std::endl;
   std::cout << head_broken<'a','b','c','d'>::value << std::endl;
   std::cout << do_head<'a','b','c','d'>::head << std::endl;
}

Я тестировал это с помощью gcc 4.4.1

Ответ 2

Насколько я понимаю, сообщение об ошибке сообщается, потому что компилятор видит объявление класса шаблона как класс с не менее чем тремя аргументами, за которыми следуют необязательные. Поскольку вы пытаетесь обратиться к нему с двумя аргументами, за которыми следует список расширений, он запутывается и выдает эту ошибку. Для правильной компиляции вам просто нужно сначала объявить шаблон следующим образом:

template <int N, typename TPack, typename... Others>
struct TypePackFirstN;

И после этого определение этапа рекурсии необходимо переформулировать как специализированную специализацию. (это делает трюк с gcc 4.5.0 20100404).

// Takes the set (First, Others...) and, while N > 0, adds (First) to TPack.
// TPack is a TypePack containing between 0 and N-1 types.
template <int N, typename TPack, typename First, typename... Others>
struct TypePackFirstN<N, TPack, First, Others...>
{
    // Error "sorry, unimplemented: cannot expand ‘Others ...’ into a fixed-length argument list" should be now gone
    typedef typename TypePackFirstN<N-1, typename TPack::template Add<First>::type, Others...>::type type;
};

// The stop condition for TypePackFirstN:  when N is 0, return the TypePack that has been built up.
template <typename TPack, typename... Others>
struct TypePackFirstN<0, TPack, Others...>         // Error "sorry, unimplemented: cannot expand ‘Others ...’ into a fixed-length argument list" should be now gone
{
    typedef TPack type;
};

Ответ 3

Какую версию GCC вы используете? В соответствии с этой страницей состояния GCC GCC 4.4 должен поддерживать ее.

Тестирование с помощью GCC 4.4.2, я получаю аналогичную ошибку.

Формулировка сообщения об ошибке кажется подразумевать, что код должен работать согласно стандарту С++ 0x, но это GCC пока не поддерживает его. Или, возможно, это ошибка компилятора?

Это правильно, GCC понимает код, но еще не может выдавить GIMPLE для него.

Что касается причины ошибки, это расширение списка переменных шаблона в другой список переменных.

Ответ 4

deft_code ответ правильный. Я публикую это на всякий случай, если это полезно, если посмотреть на боковое сравнение разбитого и фиксированного кода.

Я возьму образец кода из следующего вопроса, который кто-то разместил, который был дублирован этому и теперь закрыт: Является ли хорошим обходным решением для GCC "извините, не реализовано: не может расширять" NEXT... в список аргументов фиксированной длины" ошибка?

#include <iostream>

template <int FIRST, int... NEXT>
struct Test {
    static const int VALUE = FIRST + Test<NEXT...>::VALUE;
};

template <int FIRST>
struct Test<FIRST> {
    static const int VALUE = FIRST;
};

int main() {
    std::cout << Test<1, 2, 3>::VALUE << std::endl; // print "6"
    return 0;
}

Это, когда скомпилировано, дает:

g++ -std=c++11 -o test test.cc
test.cc:5:50: sorry, unimplemented: cannot expand âNEXT ...â into a fixed-length argument list

Но это работает (я добавил комментарии, где был изменен код):

#include <iostream>

template <int ... ALL> // Adeed
struct Test;           // Added

template <int FIRST, int... NEXT>
struct Test<FIRST, NEXT...> { // Note: specialized with <FIRST, NEXT...>
    static const int VALUE = FIRST + Test<NEXT...>::VALUE;
};

template <int FIRST>
struct Test<FIRST> {
    static const int VALUE = FIRST;
};

int main() {
    std::cout << Test<1, 2, 3>::VALUE << std::endl; // print "6"
    return 0;
}

Обратите внимание на то, что было сделано в этом трехстрочном изменении, отмеченном комментариями: изначально первый шаблон был создан специализированным шаблоном недавно добавленного вариационного шаблона.