我正在实现我自己的地图,这是一段代码。困扰我的代码的一部分是这两个声明:
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上正确编译此代码?我可以开发什么样的习惯来确保我编写的代码的可移植性?
答案 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?