Hello, there is the following class, with the following implementation:

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

When using the emplaceFront function, I get the following errors:

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

How can this problem be solved?

  • one
    Change brackets to curly Node{{std::forward<Arguments>(arguments)...}, mHead, nullptr} - Croessmah pm
  • @nick_n_a, I think it’s clear to the fool where it originated, and without pressing the button, the question was explicitly asked how to fix the error (as there is little experience with the varidaic templates), but not where to find it, and standard 17 is used, therefore it’s not worth so fooling others ... - QuickDzen pm
  • @Croessmah, thanks, the mistake was really in brackets ... - QuickDzen 1:28 pm

1 answer 1

In your case, it is enough to change brackets to curly ones:

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

Let us analyze in a simpler example what is wrong with you.

 #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); } 

When three parameters are passed to the f<First> function, the code for creating the Node<First> object is approximately as follows:

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

syntax with parentheses when creating an object results in a call to a constructor with a given set of parameters, but in Node there is no constructor with parameters. At the same time, Node is an aggregate and objects of this type can be initialized using "list initialization":

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

Next, we will look at the brackets with int_* parameters, which would have to initialize the First subobject. In this case, it is not a list of parameters, but only brackets with comma operators and operands thereof. To create an object, you need to specify the type of object being created, for example, as follows:

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

This will call a type T constructor with three parameters. But, if in our example we remove the constructor from the First type, the code will again fail to compile, since no suitable constructor. However, it is possible to initialize an object of type First with the help of the same list initialization, i.e. So:

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

Return back std::forward with a package of parameters. We get this pattern:

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

That is, now this code can work not only with types that have the necessary constructors, but also with types that can be initialized using list-initialization .