我想在函数模板中使用一个名为start
和end
的成员变量的简单结构:
#include <iostream>
using namespace std;
struct st {
int start;
int end;
};
template<typename T>
void compare(const T& v1, const T& v2){
if(v1.end < v2.end)
cout << "v1 < v2" << endl;
}
int main() {
st a = {1, 2};
st b = {2, 3};
compare(a, b);
return 0;
}
但是这个程序无法在mingw g ++ 4.8.2上编译:
main.cpp: In function 'void compare(const T&, const T&)':
main.cpp:11:11: error: parse error in template argument list
if(v1.end < v2.end)
^
main.cpp: In instantiation of 'void compare(const T&, const T&) [with T = st]':
main.cpp:18:17: required from here
main.cpp:11:5: error: 'end' is not a member template function
if(v1.end < v2.end)
^
为什么不呢?我的代码出了什么问题?
答案 0 :(得分:9)
这显然是一个gcc bug(具体来说是10200,尽管有几个dupes有很多不同的例子)。 [temp.names]声明:
当成员模板专业化的名称出现之后。或 - &gt;在后缀表达式中或之后 qualified-id 中的 nested-name-specifier , postfix-expression 的对象表达式类型相关 或 qualified-id中的嵌套名称说明符将引用为依赖类型,但该名称不是 在当前实例化(14.6.2.1)中,成员模板名称必须以关键字
template
作为前缀。 否则,名称将被假定为非模板名称。 [示例:struct X { template<std::size_t> X* alloc(); template<std::size_t> static X* adjust(); }; template<class T> void f(T* p) { T* p1 = p->alloc<200>(); // ill-formed: < means less than T* p2 = p->template alloc<200>(); // OK: < starts template argument list T::adjust<100>(); // ill-formed: < means less than T::template adjust<100>(); // OK: < starts template argument list }
-end example]
v1
和v2
是类型相关的,因此,由于省略了template
关键字,因此应该假定名称命名非模板<
应该被视为小于,完全如上例所示。
更不用说[basic.lookup.classref]声明:
首先在对象的类中查找标识符 表达。如果找不到标识符,则在整个 postfix-expression 的上下文中查找它,并命名一个类模板。
并且end
应该清楚地在对象表达式的类中找到 - 毕竟它是一个简单的成员变量。 end
仅因为与std::end()
的冲突导致失败的事实进一步支持了bug的概念,因为该范围永远不会被认为是开始的。
有趣的是,最简单的解决方案就是:don't use using namespace std;
!
答案 1 :(得分:8)
实际上,<
混淆了编译器,因为它不知道它是模板表达式的开头还是比较器。
由于@R Sahu要求官方消息来源,以下是解释:
这里重要的段落是[basic.lookup.classref] p1:
&#34;在类成员访问表达式(5.2.5)中,如果
.
或->
令牌是 紧接着是一个标识符,后跟一个&lt ;,标识符 必须抬头确定是否&lt;是一个开始 模板参数列表(14.2)或小于运算符。标识符 首先在对象表达式的类中查找。如果 找不到标识符,然后在上下文中查找 整个postfix-expression并命名一个类模板。&#34;由于v是依赖的,可能是找不到标识符所以我们 考虑如果我们查看整个环境会发生什么 后缀表达式。由于我们找到了一个函数模板,我们不应该 得出结论,我们有一个模板ID的开头。
来源:C++ confusing attribute name for member template
以下是相应的gcc
错误:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=10200
* Paolo Carlini 2015-06-23 15:03:03 UTC *
错误65878已被标记为此错误的副本。
* Colin MacKenzie 2015-06-23 22:19:29 UTC *
今天有这个。奇怪的是它在4.4.6中编译好但不在 4.8.2。
error: parse error in template argument list
Ex: assert(block.begin < block.end);
works when I parenthesize the block.begin
Ex. assert( (block.begin) < block.end);
* Paolo Carlini 2015-06-23 22:21:28 UTC *
这应该尽快修复,但我没有积极处理它 时刻。
答案 2 :(得分:0)
当成员变量为begin
或end
时,g ++ 4.8.4似乎也有问题。
使用
struct st{
int begin;
int end;
};
template <typename T>
void compare(const T& v1, const T& v2){
if(v1.begin < v2.begin)
{
cout << "v1.begin < v2.begin" << endl;
}
if(v1.end < v2.end)
{
cout << "v1.end < v2.end" << endl;
}
}
为我生成以下错误消息。
socc.cc: In function ‘void compare(const T&, const T&)’:
socc.cc:12:11: error: parse error in template argument list
if(v1.begin < v2.begin)
^
socc.cc:16:11: error: parse error in template argument list
if(v1.end < v2.end)
^
socc.cc: In instantiation of ‘void compare(const T&, const T&) [with T = st]’:
socc.cc:25:17: required from here
socc.cc:12:5: error: ‘begin’ is not a member template function
if(v1.begin < v2.begin)
^
socc.cc:16:5: error: ‘end’ is not a member template function
if(v1.end < v2.end)
也许比我更熟悉g ++开发的人可以提供更多细节。
一种解决方法是避免使用begin
和end
作为成员变量。
struct st{
int _begin;
int _end;
};
或
struct st{
int _start;
int _end;
};
答案 3 :(得分:0)
我认为,目前您可以按相反的顺序检查条件。
#include <iostream>
using namespace std;
struct st {
int start;
int end;
};
template<typename T>
void compare(const T& v1, const T& v2){
if(v2.end > v1.end) //changed the comparison order
cout << "v1 < v2" << endl;
}
int main() {
st a = {1, 2};
st b = {2, 3};
compare(a, b);
return 0;
}