While doing variadic template programming in C++11 on GCC, once in a while I get an error that says "Sorry, unimplemented: cannot expand 'Identifier...' into a fixed-length arugment list." If I remove the "..." in the code then I get a different error: "error: parameter packs not expanded with '...'".
在GCC的c++ 11中做变量模板编程时,我偶尔会遇到一个错误,说“对不起,没有实现:不能展开”标识符……“变成一个固定长度的清单。”如果我在代码中删除“…”,那么我就会得到一个不同的错误:“错误:参数包不以‘…’扩展”。
So if I have the "..." in, GCC calls that an error, and if I take the "..." out, GCC calls that an error too.
因此,如果我有“…”,GCC把它叫做错误,如果我把“…”拿出来,GCC也把它叫做错误。
The only way I have been able to deal with this is to completely rewrite the template metaprogram from scratch using a different approach, and (with luck) I eventually come up with code that doesn't cause the error. But I would really like to know what I was doing wrong. Despite Googling for it and despite much experimentation, I can't pin down what it is that I'm doing differently between variadic template code that does produce this error, and code that does not have the error.
我能够处理这个问题的唯一方法是使用另一种方法彻底重写模板元程序,而且(幸运的是)我最终生成了不会导致错误的代码。但是我真的很想知道我做错了什么。尽管我在google上搜索了它,并且进行了大量的实验,我还是不能确定我在做什么,是在生成错误的variadic模板代码和没有错误的代码之间做了不同的事情。
The wording of the error message seems to imply that the code should work according the C++11 standard, but that GCC doesn't support it yet. Or perhaps it is a compiler bug?
错误消息的措辞似乎暗示代码应该按照c++ 11标准工作,但是GCC还不支持它。或者它可能是一个编译器错误?
Here's some code that produces the error. Note: I don't need you to write a correct implementation for me, but rather just to point out what is about my code that is causing this specific error
下面是一些产生错误的代码。注意:我不需要您为我编写一个正确的实现,而是要指出导致这个特定错误的代码是什么
// 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;
};
EDIT: I've noticed that while a partial template instantiation that looks like does incur the error:
编辑:我注意到,虽然局部模板实例化看起来会导致错误:
template <typename... T>
struct SomeStruct<1, 2, 3, T...> {};
Rewriting it as this does not produce an error:
重写它,因为它不会产生错误:
template <typename... T>
struct SomeStruct<1, 2, 3, TypePack<T...>> {};
It seems that you can declare parameters to partial specializations to be variadic; i.e. this line is OK:
似乎可以将参数的部分专门化声明为变量;也就是说,这行可以:
template <typename... T>
But you cannot actually use those parameter packs in the specialization, i.e. this part is not OK:
但在专门化中不能实际使用这些参数包,也就是说,这部分不行:
SomeStruct<1, 2, 3, T...>
The fact that you can make it work if you wrap the pack in some other type, i.e. like this:
如果你用其他类型的包装,比如:
SomeStruct<1, 2, 3, TypePack<T...>>
to me implies that the declaration of the variadic parameter to a partial template specialization was successful, and you just can't use it directly. Can anyone confirm this?
在我看来,将变量参数声明为局部模板专门化是成功的,您不能直接使用它。谁能证实这一点?
4 个解决方案
#1
48
There is a trick to get this to work with gcc. The feature isn't fully implemented yet, but you can structure the code to avoid the unimplemented sections. Manually expanding a variadic template into a parameter list won't work. But template specialization can do that for you.
有一个技巧可以让这个方法与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;
}
I tested this with gcc 4.4.1
我用gcc 4.4.1测试了这个
#2
3
As far as I understand the error is reported because the compiler sees the declaration of the template class as a class with at least 3 arguments followed by the optional ones. Since you try to refer to it with 2 arguments followed by the expansion list, it gets confused and issues this error. In order to make it compile correctly you just need to first declare the template like this:
据我所知,报告错误是因为编译器将模板类的声明视为一个类,其中至少包含3个参数,然后是可选参数。由于您试图使用扩展列表后面的两个参数来引用它,它会感到混乱并发出此错误。为了使它正确地编译,您只需首先声明如下模板:
template <int N, typename TPack, typename... Others>
struct TypePackFirstN;
And after that the recursion step definition has to be restated as a template specialization. (this does the trick with gcc 4.5.0 20100404).
然后递归步骤定义必须重新声明为模板专门化。(这是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
2
What version of GCC are you using? According to this GCC status page, GCC 4.4 should support it.
您正在使用哪个版本的GCC ?根据GCC状态页面,GCC 4.4应该支持它。
Testing with GCC 4.4.2, I get similar error.
在使用GCC 4.4.2进行测试时,我得到了类似的错误。
The wording of the error message seems to imply that the code should work according the C++0x standard, but that GCC doesn't support it yet. Or perhaps it is a compiler bug?
错误消息的措辞似乎暗示代码应该按照c++ 0x标准工作,但是GCC还不支持它。或者它可能是一个编译器错误?
This is correct, GCC understands the code but cannot yet spit out GIMPLE for it.
这是正确的,GCC理解代码,但还不能为它输出GIMPLE。
As for what causes the error, it's the extension of template variable list to another template's list of variables.
至于导致错误的原因,是模板变量列表扩展到另一个模板的变量列表。
#4
1
deft_code's answer is correct. I'm posting this just in case this it's helpful seeing a side-by-side comparison of broken versus fixed code.
deft_code的回答是正确的。我发布这篇文章是为了防止这篇文章的出现,它能帮助我们看到破碎代码和固定代码的对比。
I'll take the code sample from the following question someone posted that was duplicated to this one and is now closed: Is ther a good workaround for GCC's "sorry, unimplemented: cannot expand ‘NEXT ...’ into a fixed-length argument list" error?
我将从以下问题中提取代码示例,这个问题是别人贴出来的,现在已经被关闭了。将“错误”放入一个固定长度的参数列表中?
#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;
}
This, when compiled, gives:
当编译,这给:
g++ -std=c++11 -o test test.cc
test.cc:5:50: sorry, unimplemented: cannot expand âNEXT ...â into a fixed-length argument list
But this works (I added comments where code was changed):
但这是可行的(我在修改代码的地方添加了注释):
#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;
}
Note what was done in this three line change marked by the comments: what was originally the first template was made a specialized template of the newly added variadic template.
请注意在注释中标记的这三行更改中做了什么:最初的第一个模板是新添加的variadic模板的专门化模板。
#1
48
There is a trick to get this to work with gcc. The feature isn't fully implemented yet, but you can structure the code to avoid the unimplemented sections. Manually expanding a variadic template into a parameter list won't work. But template specialization can do that for you.
有一个技巧可以让这个方法与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;
}
I tested this with gcc 4.4.1
我用gcc 4.4.1测试了这个
#2
3
As far as I understand the error is reported because the compiler sees the declaration of the template class as a class with at least 3 arguments followed by the optional ones. Since you try to refer to it with 2 arguments followed by the expansion list, it gets confused and issues this error. In order to make it compile correctly you just need to first declare the template like this:
据我所知,报告错误是因为编译器将模板类的声明视为一个类,其中至少包含3个参数,然后是可选参数。由于您试图使用扩展列表后面的两个参数来引用它,它会感到混乱并发出此错误。为了使它正确地编译,您只需首先声明如下模板:
template <int N, typename TPack, typename... Others>
struct TypePackFirstN;
And after that the recursion step definition has to be restated as a template specialization. (this does the trick with gcc 4.5.0 20100404).
然后递归步骤定义必须重新声明为模板专门化。(这是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
2
What version of GCC are you using? According to this GCC status page, GCC 4.4 should support it.
您正在使用哪个版本的GCC ?根据GCC状态页面,GCC 4.4应该支持它。
Testing with GCC 4.4.2, I get similar error.
在使用GCC 4.4.2进行测试时,我得到了类似的错误。
The wording of the error message seems to imply that the code should work according the C++0x standard, but that GCC doesn't support it yet. Or perhaps it is a compiler bug?
错误消息的措辞似乎暗示代码应该按照c++ 0x标准工作,但是GCC还不支持它。或者它可能是一个编译器错误?
This is correct, GCC understands the code but cannot yet spit out GIMPLE for it.
这是正确的,GCC理解代码,但还不能为它输出GIMPLE。
As for what causes the error, it's the extension of template variable list to another template's list of variables.
至于导致错误的原因,是模板变量列表扩展到另一个模板的变量列表。
#4
1
deft_code's answer is correct. I'm posting this just in case this it's helpful seeing a side-by-side comparison of broken versus fixed code.
deft_code的回答是正确的。我发布这篇文章是为了防止这篇文章的出现,它能帮助我们看到破碎代码和固定代码的对比。
I'll take the code sample from the following question someone posted that was duplicated to this one and is now closed: Is ther a good workaround for GCC's "sorry, unimplemented: cannot expand ‘NEXT ...’ into a fixed-length argument list" error?
我将从以下问题中提取代码示例,这个问题是别人贴出来的,现在已经被关闭了。将“错误”放入一个固定长度的参数列表中?
#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;
}
This, when compiled, gives:
当编译,这给:
g++ -std=c++11 -o test test.cc
test.cc:5:50: sorry, unimplemented: cannot expand âNEXT ...â into a fixed-length argument list
But this works (I added comments where code was changed):
但这是可行的(我在修改代码的地方添加了注释):
#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;
}
Note what was done in this three line change marked by the comments: what was originally the first template was made a specialized template of the newly added variadic template.
请注意在注释中标记的这三行更改中做了什么:最初的第一个模板是新添加的variadic模板的专门化模板。