【问题标题】:MSVC compiler error involving nested templates涉及嵌套模板的 MSVC 编译器错误
【发布时间】:2012-08-11 22:24:46
【问题描述】:

以下代码可使用 GCC 4.7 和 clang 3.0 编译,但不能使用 MSVC 10:

template <typename X>
struct point
{
    template <typename Seq>
    struct point_iterator
    {
        template <typename T> 
        struct deref;

        template <typename Sq>
        struct deref<point_iterator<Sq> >
        {
        };
    };
};

int main()
{
    typedef point<int> point_t;
    typedef point_t::point_iterator<point_t> Iterator;
    Iterator::deref<Iterator> m;
}

MSVC 给出的编译器错误是:

test.cpp
testcpp(21) : error C2079: 'm' uses undefined struct 'point<X>::point_iterator<Seq>::deref<T>'
        with
        [
            X=int,
            Seq=point_t
        ]
        and
        [
            T=Iterator
        ]

我认为应该定义有问题的类型,因为它应该匹配deref 的部分特化。

  1. 这是有效的代码吗?如果是这样,并且拒绝它是 MSVC 的一个错误,有人知道这个错误是否已经被报告了吗?
  2. 如果是错误,有没有人知道解决方法?

【问题讨论】:

  • 我尝试添加 template&lt;&gt; struct Q&lt;R&lt;S&lt;T&gt; &gt; &gt;; 并且 MSVC8 以内部编译器错误奖励我...

标签: c++ templates visual-c++ compiler-errors nested-class


【解决方案1】:

问题是 MSVC10 不同意你专门化了这个类。如果你试试这个

    template <typename Sq>
    struct deref
    {
       typedef int basic;
    };

    template <typename Sq>
    struct deref<point_iterator<Sq> >
    {
       typedef int special;
    };

你会发现 Iterator::deref&lt;Iterator&gt;::special 存在于 gcc 中,但存在于 MS 中只有 basic 存在。

我确实认为您的代码是正确的。

似乎只有部分专业化受到影响。专注于int 按预期工作。

【讨论】:

  • 在 Visual Studio Express 2010 中,这些都不起作用(注意:在封闭类之外的上下文中,例如在 main 中)工作:Iterator::deref&lt;point_t::point_iterator&lt;point_t&gt; &gt;::special qq; Iterator::deref&lt;point_t::point_iterator&lt;int&gt; &gt;::special qq;
【解决方案2】:

您的原始代码

以下在 Visual C++ Express 2010 上编译但生成错误答案:

#include <iostream>

template <typename X>
struct point
{
    template <typename Seq>
    struct point_iterator
    {
        template <typename> 
        struct deref 
        {
                static const int value = 0;
        };

        template <typename Sq>
        struct deref< point_iterator<Sq> > 
        { 
                static const int value = 1; 
        };
    };
};

int main()
{
    typedef point<int> point_t;
    typedef point_t::point_iterator<point_t> Iterator;
    int v = Iterator::deref<Iterator>::value;
    std::cout << v << "\n"; // prints 1 on gcc 4.5.1, prints 0 on VC++ 2010
    return 0;
}

这解释了你得到的编译错误:VC++ 试图实例化你没有定义的主deref 模板。在这一点上,看起来 VC++ 是错误的,而 gcc / clang 是优秀的编译器。

中间“解决方案”

但是,将 point_iterator 及其嵌套的 deref 移动到外部命名空间 detail 会为 gcc 和 VC++ 提供相同但错误的结果。调用 detail::point_iterator::deref 给出了你想要的结果,但是在 point 中调用嵌套的 point_iterator 现在对于 gcc 和 VC++ 都会给出错误的结果!显然,嵌套模板通常对编译器来说很难。

#include <iostream>

namespace detail {
    template <typename Seq>
    struct point_iterator
    {
        template <typename> 
        struct deref 
        {
                static const int value = 0;
        };

        template <typename Sq>
        struct deref< point_iterator<Sq> > 
        { 
                static const int value = 1; 
        };
    };
}

template<typename X>
struct point
{
        template<typename Seq>
        struct point_iterator
        :
                public detail::point_iterator<Seq>
        {};
};

int main()
{
    typedef point<int> point_t;

    typedef point_t::point_iterator<point_t> Iterator1;
    int v1 = Iterator1::deref<Iterator1>::value;
    std::cout << v1 << "\n"; // prints 0 on both gcc 4.5.1 and VC++ 2010

    typedef detail::point_iterator<point_t> Iterator2;
    int v2 = Iterator2::deref<Iterator2>::value;
    std::cout << v2 << "\n"; // prints 1 on both gcc 4.5.1 and VC++ 2010

    return 0;
}

推荐的方法

嵌套模板还有其他问题:如果不专门化外部模板,就不能明确专门化内部模板(具有讽刺意味的是,VC++ 确实允许这种非标准功能!)。这可以通过使用虚拟模板参数并对其进行部分特化来解决(尽管如果您给这个虚拟模板参数一个默认值,VC++ 将要求抑制虚假的编译器警告)。

因此,我的建议是解开您获得的整个嵌套模板层次结构,并简单地定义 3 个单独的模板,并根据您的喜好对它们进行专门化。这将产生最简单的代码并且不会产生任何意外。

#include <iostream>

template<typename X>
struct point {};

template <typename Seq>
struct point_iterator {};

template <typename> 
struct deref 
{
        static const int value = 0;
};

template <typename Sq>
struct deref< point_iterator<Sq> > 
{ 
        static const int value = 1; 
};

int main()
{
    typedef point<int> point_t;
    typedef point_iterator<point_t> Iterator1;
    int v = deref<Iterator1>::value;
    std::cout << v << "\n"; // prints 1 on both gcc 4.5.1 and VC++ 2010

    return 0;
}

【讨论】:

  • 第二个例子的输出没有错。 deref 的部分特化仅适用于 detail::point_iterator&lt;...&gt;,主模板适用于其他所有内容。当您使用point::point_iterator&lt;...&gt; 实例化它时,主模板是更好的匹配,因为point::point_iterator&lt;...&gt;detail::point_iterator&lt;...&gt; 不同(它只是从它派生而来)。特化不适用于特化类型的派生类型,仅适用于特化类型本身。
  • 总体结论是,这是 MSVC 的一个错误,应该修复。我已经向微软报告了这个错误,他们同意这是一个错误:social.msdn.microsoft.com/Forums/en-US/vclanguage/thread/…
  • @HighCommander4 只是出于好奇:template&lt;typename X, typename dummy = void&gt; struct BB&lt;X, A&lt;U&gt; &gt; 不是更简单的方法来获得唯一的B,具体取决于“嵌入”类A&lt;U&gt;
  • 好点,这为每个“嵌入”类提供了一个独特的B,而无需使用嵌套。不过,嵌套更受欢迎还有其他原因。例如,它避免使用附加名称污染封闭的命名空间。就我而言,我正在编写一个用于类范围的宏,并扩展为嵌套类的定义(称为B)。 B 包含它自己的嵌套类,称之为C。用户请求名称 B 作为宏参数之一,但名称 C 是一个实现细节。 [下文继续]
  • 因此最好将C 隐藏为B 中的嵌套类。然而,为了解决这个错误,我必须在与B 相同的范围内声明C,并修改其名称以避免与客户端代码中相同范围内的另一个声明冲突。如果宏打算在命名空间范围内使用,我可以打开一个实现命名空间来容纳C,但我不能在类范围内这样做。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-10
  • 2012-03-02
  • 2010-10-18
  • 2020-05-21
  • 1970-01-01
  • 1970-01-01
  • 2021-02-25
相关资源
最近更新 更多