C++ Variadic templates

120
30 ноября 2019, 18:10

Здравствуйте есть следующий класс, со следующей реализацией:

template<typename Type>
class List : public Memory::INonCopyable
{
    struct Node
    {
        Type value;
        Node* next;
        Node* prev;
    };
public:
    explicit List(std::uint8_t countVirtualPages = 1) noexcept
        : mAllocator(countVirtualPages, sizeof(Node)) {}
    template<typename... Arguments>
    void emplaceFront(Arguments&&... arguments) noexcept;
    ...
private:
    std::size_t mSize = 0;
    Node* mHead = nullptr;
    Node* mTail = nullptr;
    Memory::Allocators::PoolAllocator mAllocator;
};
template<typename Type>
template<typename... Arguments>
void List<Type>::emplaceFront(Arguments&&... arguments) noexcept 
{
    void* memoryForNode = mAllocator.allocate(sizeof(Node));
    Node* newNode = new (memoryForNode) Node((std::forward<Arguments>(arguments)...), mHead, nullptr);
    if (isEmpty()) 
    {
        mHead = mTail = newNode;
    }
    else 
    {
        mHead->prev = newNode;
        mHead = newNode;
    }
}

При использовании функции emplaceFront получаю следующие ошибки:

Error   C2760   syntax error: unexpected token '...', expected ')'  
Error   C3520   'arguments': parameter pack must be expanded in this context
Error   C2059   syntax error: '...'

Как можно решить эту проблему?

Answer 1

В Вашем случае достаточно поменять скобочки на фигурные:

Node{{std::forward<Arguments>(arguments)...}, mHead, nullptr}

Разберем на более простом примере что же у Вас не так.

#include <utility>
template<typename T>
struct Node
{
    T value;
    void * unused;
};

template<typename T, typename ... Args>
void f(Args && ... args)
{
    delete new Node<T>((std::forward<Args>(args)...), nullptr);
}
struct First
{
    First(int, int, int) {}
    int m1;
    int m2;
    int m3;
};
int main()
{
    f<First>(1, 2, 3);
}

При передаче в функцию f<First> трех параметров получается примерно следующий код создания объекта Node<First>:

//int_* - параметры функции после распаковки пакета параметров
//std::forward опущен, т.к. в данном случае он не столь важен и не играет роли.
Node<First>((int_1, int_2, int_3), nullptr);

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

Node<First>{(int_1, int_2, int_3), nullptr};

Далее посмотрим на скобки с параметрами int_*, которые должны были бы инициализировать подъобъект First. В данном случае это не список параметров, а всего лишь скобки с операторами "запятая" и операндами оного. Для создания объекта нужно указать тип создаваемого объекта, например, так:

Node<T>{T(int_1, int_2, int_3), nullptr};

Это приведет к вызову конструктора типа T с тремя параметрами. Но, если в нашем примере у типа First убрать конструктор, то код снова откажется компилироваться, т.к. отсутствует подходящий конструктор. Однако, объект типа First возможно инициализировать с помощью всё той же списочной инициализации, т.е. так:

Node<T>{{int_1, int_2, int_3}, nullptr};

Вернем назад std::forward с пакетом параметров. Получим такой шаблон:

template<typename T, typename ... Args>
void f(Args && ... args)
{
    delete new Node<T>{{std::forward<Args>(args)...}, nullptr};
}

То есть теперь данный код может работать не только с типами, имеющими необходимые конструкторы, но и с типами, которые можно инициализировать с помощью list-initialization.

READ ALSO
Поиск заданной области в окне

Поиск заданной области в окне

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

110
Как узнать тип специализации переменной?

Как узнать тип специализации переменной?

Теперь, нужно объявить переменную b такого типа, который является специализацией a(В данном случае int, а если бы было A< double > a, то b должна была...

110