具有嵌套模板类的模板类,c ++编译好,vs2013将无法编译

时间:2014-12-21 19:23:58

标签: c++ templates visual-studio-2013 g++ nested-class

我正在实现我自己的地图,这是一段代码。困扰我的代码的一部分是这两个声明:

template<typename KEY, typename VAL>
Map<KEY,VAL>::MapPair<KEY,VAL> Map<KEY,VAL>::make_map_pair(KEY k, VAL v){
    return MapPair<KEY,VAL>(k,v);
 }

template<typename KEY, typename VAL>
template<typename K, typename V>
Map<KEY,VAL>::MapPair<K,V>& Map<KEY,VAL>::MapPair<K,V>::setKey(K keyp, V val){
    key = keyp;
    value = val;
 }

类def如下:

 template <typename KEY, typename VAL>
 class Map{
 private:
    template<typename K, typename V>
    class MapPair {
    public:
        K key;
        V value;
        MapPair(){};
        MapPair(K key, V value);
        MapPair<K,V>& setKey(K key, V val);
        V& getValue();
        K getKey();
        bool operator==(MapPair<K,V> item);
    };

    List<MapPair<KEY,VAL>> pair_list_;
    MapPair<KEY,VAL> make_empty_map_pair(KEY k);
    MapPair<KEY,VAL> make_map_pair(KEY k, VAL v);
 public:
    Map(){}
    bool exists(KEY key);
    VAL& operator[](KEY key);
    VAL pop_pair(KEY key);
 };

此代码编译时没有窥视g ++,但Visual Studio 2013会删除错误:

error C2059: syntax error : ')'
error C2059: syntax error : ')'

每个顶部函数定义都会产生错误。

来自g ++的debain 7.5

g++ -Wall -c -std=c++11  vm.cpp
g++ vm.o -o vm

并且它运行良好,没有运行时问题,并做了它应该做的事情。

问题是为什么g ++运行得很好,vs2013不会编译它?如何在vs2013上正确编译此代码?我可以开发什么样的习惯来确保我编写的代码的可移植性?

2 个答案:

答案 0 :(得分:4)

事实证明,VS2013喜欢在嵌套类的返回类型之前插入typename。我知道我'可以'那样做但是我永远不知道为什么我会这样做。

template<typename KEY, typename VAL>
typename Map<KEY,VAL>::MapPair<KEY,VAL> Map<KEY,VAL>::make_map_pair(KEY k, VAL v){
/*^^^^^ right here before the nested class object that it will return.*/
    return MapPair<KEY,VAL>(k,v);
}

最好的想法是使用它,以便代码可以移植更少的更改,即使g ++不需要它。

答案 1 :(得分:2)

我阅读C ++ 11标准表明需要typename。 VS2013正在做正确的事。

如果没有typename关键字,则假定从属名称不命名类型。

  

14.6名称解析[temp.res]

     

2)   模板声明或定义中使用的名称,取决于模板参数   假定不命名类型,除非适用的名称查找找到类型名称或名称是合格的   通过关键字typename。

     

3)   当qualified-id旨在引用不是当前实例化成员的类型时   并且它的嵌套名称说明符引用依赖类型,它应以关键字typename为前缀

     

7)   在类模板的定义内或定义在类模板的成员之后   declarator-id ,在引用先前声明的名称时不需要关键字typename   声明类型的类模板的成员。 [注意:这些名称可以使用不合格的名称找到   查找,类成员查找当前实例化或类成员访问   表达式查找当对象表达式的类型是当前实例化时

     

14.6.2.1依赖类型[temp.dep.type]

     

名称是指当前实例化,如果它是

     
      
  • 在主类模板的定义或主类模板的成员中,名称   类模板后跟主模板的模板参数列表(如下所述)   括在&lt;&gt;
  • 中   

Map<KEY, VAL>的定义中使用Map时,它指的是当前实例化。在解析make_map_pair的定义时,Map<KEY, VAL>::限定的类型名称通常可以通过类成员名称查找到当前实例化来找到。

但是,当C ++解析器在成员函数定义的返回类型中遇到Map<KEY, VAL>时 - 在declarator-id之前 - 它还没有遇到封闭类的名称。此时,解析器无法确定Map是否引用了封闭类。

出于这个原因 - 无论Map<KEY, VAL>是否命名当前实例化 - 标准都不允许在声明者id之前在类模板成员的定义中省略typename

Vaughn Cato的这个example表明Clang / GCC的行为不一致,在类似的场景中需要typename

template <typename T>
struct A {
    typedef int X;
    X f();
};

template <typename T>
A<T>::X A<T>::f() // error: missing 'typename'
{
}

如果我们认为名称Map是相关的并且typename是必需的,则还需要template关键字:

  

14.2模板专精的名称[temp.names]

     

当成员模板专精的名称出现在后缀表达式中的.->之后或之后   限定id中的嵌套名称说明符,以及后缀表达式的对象或指针表达式   qualified-id中的nested-name-specifier取决于模板参数(14.6.2),但不引用a   当前实例化的成员(14.6.2.1),成员模板名称必须以关键字为前缀   模板。否则,假定该名称命名非模板。

更正后的例子是:

template<typename KEY, typename VAL>
typename Map<KEY,VAL>::template MapPair<KEY,VAL>
    Map<KEY,VAL>::make_map_pair(KEY k, VAL v) {
    return MapPair<KEY,VAL>(k,v);
}

此处讨论了同一问题:Can typename be omitted in the type-specifier of an out of line member definition?