【问题标题】:GCC error with variadic templates: "Sorry, unimplemented: cannot expand 'Identifier...' into a fixed-length argument list"可变参数模板的 GCC 错误:“抱歉,未实现:无法将 '标识符...' 扩展为固定长度的参数列表”
【发布时间】:2010-12-31 15:58:43
【问题描述】:

在 GCC 上使用 C++11 进行可变参数模板编程时,偶尔会收到一条错误消息,提示“抱歉,未实现:无法将 '标识符...' 扩展为固定长度的参数列表。”如果我删除代码中的“...”,则会收到另一个错误:“错误:参数包未使用 '...' 展开”。

因此,如果我输入了“...”,则 GCC 称其为错误,如果我删除“...”,则 GCC 也称其为错误。

我能够处理这个问题的唯一方法是使用不同的方法从头开始完全重写模板元程序,并且(幸运的是)我最终想出了不会导致错误的代码。但我真的很想知道我做错了什么。尽管谷歌搜索并进行了大量实验,但我无法确定我在产生此错误的可变参数模板代码和没有错误的代码之间所做的不同。

错误消息的措辞似乎暗示代码应该按照 C++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...>>

对我来说意味着对部分模板特化的可变参数的声明是成功的,你只是不能直接使用它。谁能证实这一点?

【问题讨论】:

  • 现在可以使用clang 3.2

标签: c++ templates g++ metaprogramming c++11


【解决方案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 对此进行了测试

【讨论】:

  • 这实际上应该是最好的答案。专业化是 gcc 4.4 的必经之路。
  • 我将其更改为已接受的答案,因为我实际上只是使用了此答案中的解决方法,它解决了我的问题。请注意,如果您有很多模板代码,则使用此解决方法似乎会消耗更多的编译时间/大小。然而,在我发现这增加了我的构建大小/时间之后,我发现我能够将它汇总到我已经拥有的已经专业化的外部模板中,因此使用专业化来分摊成本以解决问题。跨度>
  • 你能解释一下偏特化在这里有什么特殊效果,这样bug现在就消失了,或者我应该问一下偏特化是如何解决这个问题的,非常感谢,等待回复?
  • GCC 有处理可变参数模板的代码。然而,GCC 4.4 至少缺少将可变参数模板参数扩展为具有一些固定参数的模板的代码(例如broken_head)。一旦你通过了未实现的部分(以另一种方式扩展它),其余的可变参数就可以正常工作。特化只是避免强制编译器直接扩展all... 的一种方式。它可以将其扩展为没有固定参数的类型,然后专门化代码可以选择最适合的类型(以head为固定参数的类型)。
  • 你的这一行“一旦你通过了未实现的部分(以另一种方式扩展),其余的可变参数都可以正常工作”并且下面的行超出了我破碎的头脑:D ,您能否通过举一些例子来解释这个过程,即部分专业化解决了这个问题? ,可能是通过编辑您的答案? .非常感谢:)
【解决方案2】:

据我了解,报告错误是因为编译器将模板类的声明视为具有至少 3 个参数后跟可选参数的类。由于您尝试使用后跟扩展列表的 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 status page,GCC 4.4 应该支持它。

    使用 GCC 4.4.2 进行测试,我得到了类似的错误。

    错误信息的措辞似乎 暗示代码应该可以工作 根据 C++0x 标准,但是 GCC 还不支持它。也许 是编译器错误?

    这是正确的,GCC 理解代码但还不能为它吐出 GIMPLE。

    至于导致错误的原因,是模板变量列表扩展到另一个模板的变量列表。

    【讨论】:

    • 您能否更全面地解释将模板变量列表扩展到另一个模板的列表意味着什么?我开始认为这与不能直接使用可变参数模板包作为模板特化的参数有关。
    • 如果包扩展创建了一个模板参数列表,其中一些参数命中固定参数,而一些参数命中模板参数包,那么似乎确实会发生此错误。我重写了你的代码,它确实有效:codepad.org/IBT60IbJ(我不得不引入另一个部分专业化,以使 &lt;0, A, B&gt; 案例选择一个 uniq 专业化)。在这一个中,每个包扩展总是只命中一个相应的参数包。
    【解决方案4】:

    deft_code 的答案是正确的。我发布此内容以防万一看到损坏代码与固定代码的并排比较会有所帮助。

    我将从某人发布的以下问题中获取代码示例,该问题与此问题重复,现已关闭: 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;
    }
    

    编译后会给出:

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

    但这有效(我在更改代码的地方添加了 cmets):

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

    注意由 cmets 标记的这三行更改中做了什么:最初的第一个模板变成了新添加的可变参数模板的专用模板。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-29
      • 2014-04-12
      • 2012-07-03
      • 1970-01-01
      • 2017-05-28
      相关资源
      最近更新 更多