涉及嵌套模板的MSVC编译器错误

时间:2012-08-11 22:24:47

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

以下代码使用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. 如果这是一个错误,有没有人知道它的解决方法?

2 个答案:

答案 0 :(得分:0)

问题是MSVC10不同意您专门使用该类。如果你试试这个

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

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

您会发现Iterator::deref<Iterator>::special与gcc一起存在,但只有MS存在basic

我认为你的代码是正确的。

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

答案 1 :(得分:0)

您的原始代码

以下在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_iterator内调用嵌套point现在会为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;
}