【问题标题】:Partial template specialization for variadic types and variadic argument packs expanded into outer type result in ambiguity可变参数类型的部分模板特化和扩展为外部类型的可变参数包导致歧义
【发布时间】:2012-04-05 00:07:22
【问题描述】:

我正在尝试使用 g++-4.7 (20120228-1) 编译以下程序:

#include <cstdlib>
#include <tuple>

template<typename X> struct Y {};

template<typename T, size_t Level, size_t TermLevel> struct A;

// (B) dummy for T=tuple<int, Ts...> just to show it works for simple expansions
template<typename ... Ts, size_t Level, size_t TermLevel>
struct A<std::tuple<int, Ts...>, Level, TermLevel>
{
    A<std::tuple<int, Ts...>, Level+1, TermLevel> value;
};

template<typename ... Ts, size_t Level>
struct A<std::tuple<int, Ts...>, Level, Level> {};


// (C) ambiguous partial specialization
template<typename ... Ts, size_t Level, size_t TermLevel>
struct A<std::tuple<Y<Ts>...>, Level, TermLevel>
{
    A<std::tuple<Y<Ts>...>, Level+1, TermLevel> value;
};

template<typename ... Ts, size_t Level>
struct A<std::tuple<Y<Ts>...>, Level, Level> {};


int main(int argc, const char *argv[])
{
    A<std::tuple<int, float, int>, 0, 5> tint;
    A<std::tuple<Y<int>, Y<float>>, 0, 1> tn;
    return 0;
}

导致歧义如下:

g++-4.7 -g -O0 -std=c++0x    specialization_orig.cc   -o specialization_orig
specialization_orig.cc: In instantiation of 'struct A<std::tuple<Y<int>, Y<float> >, 0ul, 1ul>':
specialization_orig.cc:33:43:   required from here
specialization_orig.cc:23:49: error: ambiguous class template instantiation for 'struct A<std::tuple<Y<int>, Y<float> >, 1ul, 1ul>'
specialization_orig.cc:21:8: error: candidates are: struct A<std::tuple<Y<Ts>...>, Level, TermLevel>
specialization_orig.cc:27:8: error:                 struct A<std::tuple<Y<Ts>...>, Level, Level>
specialization_orig.cc:23:49: error: 'A<std::tuple<Y<Ts>...>, Level, TermLevel>::value' has incomplete type
specialization_orig.cc:6:61: error: declaration of 'struct A<std::tuple<Y<int>, Y<float> >, 1ul, 1ul>'

这有点奇怪,因为可变参数扩展适用于参数包的简单扩展,但一旦可变参数包扩展为嵌套在其他模板类型中,就会失败。

这仅仅是编译器的疯狂还是我做错了什么?

【问题讨论】:

  • ICC 也拒绝此代码,并出现“错误:多个部分特化匹配类的模板参数列表”A<:tuple>, Y>, 1UL , 1UL>" "A<:tuple>...>, Level, TermLevel>" "A<:tuple>...>, Level, Level>" A...>, Level+1, TermLevel> value;”,但 CLang 接受它。任何有 Comeau 或足够新的 VC++ 的人都可以试试这个,因为我无法访问 VS2013,而且 Comeau 的试用已经失败?

标签: c++ templates c++11 template-specialization


【解决方案1】:

它看起来像一个 GCC 错误,因为如果将参数包替换为固定数量的参数,歧义就会消失:http://ideone.com/6D4Fi

这种歧义也可以使用std::enable_if来解决。

/* add "typename = void" anonymous parameter which enables individual partial
   specializations by matching the void result in std::enable_if<>::type. */
template<typename T, size_t Level, size_t TermLevel, typename = void> struct A;

// No enable_if needed here: always enabled if the levels are equal.
template<typename ... Ts, size_t Level>
struct A<std::tuple<int, Ts...>, Level, Level, void> {}; // just pass void

// Use enable_if to disable in case the levels are equal.
template<typename ... Ts, size_t Level, size_t TermLevel>
struct A<std::tuple<Y<Ts>...>, Level, TermLevel,
    typename std::enable_if< Level != TermLevel >::type >

【讨论】:

  • 当然,enable_if 始终是一个选项。即使我认为在这种情况下应该没有必要。首先是因为将 (C) 专门用于 template&lt;typename ... Ts&gt; struct A&lt;std::tuple&lt;Y&lt;Ts&gt;...&gt;, 1, 1&gt; {}; 也无济于事,其次因为它不会抱怨 (B) 在您的解释之后应该会产生同样的歧义
  • @ignatz:啊,原来如此!必须有一个规则,即具有较少模板参数的函数更专业化,否则它们将同样专业化。我现在没有时间复习:v(。无论如何,我会选择enable_if,因为它是一种权宜之计,显而易见的修复。我认为你刚刚偶然发现了一个 GCC 错误。如果你消除它也会消失参数包:ideone.com/6D4Fi
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-08-13
  • 2012-03-27
  • 2013-09-11
  • 2012-01-14
  • 2011-09-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多