【高手接分】200分问一个关于namespace/name lookup的问题

visame 2008-12-04 12:41:15
问题源于一个同学的提问:

我在自己的模块中定义了一个用于比较std::bitset大小的模板函数:
template<size_t _N>
bool operator<(const std::bitset<_N> &lhs,const std::bitset<_N> &rhs)
{
return lhs.to_string() < rhs.to_string();
}
如果程序中对两个bitset变量比较大小,没有问题,但我比较两个
std::pair<bitset<N>,bitset<N> >类型的变量时,就会提示没有可匹配的operator<,
那位大牛给指点一下原因是什么?先谢过了:)


#include <bitset>
#include <utility>
#include <iomanip>
#include <iostream>

using namespace std;

template <size_t N>
bool operator<(const bitset<N> & lhs,const bitset<N>& rhs)
{
return lhs.count()<rhs.count();
}
pair<bitset<100>,bitset<100> > a,b;
int main()
{
bitset<100> l(10),r(20);
cout<<(l<r)<<endl;
a=make_pair(r,r);
b=make_pair(l,l);
cout<<(a<b)<<endl;
}

编译器为gcc version 3.2.2 20030222 (Red Hat Linux 3.2.2-5)

编译错误如下:
/usr/include/c++/3.2.2/bits/stl_pair.h: In function `bool std::operator<(const
std::pair<_T1, _T2>&, const std::pair<_T1, _T2>&) [with _T1 =
std::bitset<100>, _T2 = std::bitset<100>]':
XXX.cpp:20: instantiated from here
/usr/include/c++/3.2.2/bits/stl_pair.h:102: no match for `const
std::bitset<100>& < const std::bitset<100>&' operator
/usr/include/c++/3.2.2/bits/stl_pair.h:102: no match for `const
std::bitset<100>& < const std::bitset<100>&' operator
/usr/include/c++/3.2.2/bits/stl_pair.h:102: no match for `const
std::bitset<100>& < const std::bitset<100>&' operator

于是做了一个实验:

namespace test{
class A{//means bitset<>
public:
int aa;
};
template <class T>
class B{//means pair<>
public:
T BT;
};

template <class T>
bool operator<(B<T> a,B<T> b)
{
return a.BT<b.BT;
}
}


bool operator<(test::A a,test::A b)
{
return a.aa<b.aa;
}

int main()
{
test::B<test::A> B1,B2;
B1<B2;
return 0;
}

得到错误如下:

TTT.cpp: In function `bool test::operator<(test::B<T>, test::B<T>) [with T =
test::A]':
TTT.cpp:28: instantiated from here
TTT.cpp:15: no match for `test::A& < test::A&' operator


增加一个针对B的特例化的operator<就可以解决。代码如下:

namespace test{
class A{//means bitset
public:
int aa;
};
template <class T>
class B{//means pair
public:
T BT;
};
template <class T>
bool operator<(B<T> a,B<T> b)
{
return a.BT<b.BT;
}
}


bool operator<(test::A a,test::A b)
{
return a.aa<b.aa;
}
//HERE!!!
bool operator<(test::B<test::A> a,test::B<test::A> b)
{
return a.BT<b.BT;
}

int main()
{
test::B<test::A> B1,B2;
B1<B2;
return 0;
}


这到底是为什么呢?
为什么当初找不到bool operator<(test::A a,test::A b)呢?
...全文
272 36 打赏 收藏 转发到动态 举报
写回复
用AI写文章
36 条回复
切换为时间正序
请发表友善的回复…
发表回复
walfud 2012-01-10
  • 打赏
  • 举报
回复
虽然是老帖了, 我还是想说说, 你在

#include <bitset>
#include <utility>
#include <iomanip>
#include <iostream>

using namespace std;

template <size_t N>
bool operator<(const bitset<N> & lhs,const bitset<N>& rhs)
{
return lhs.count()<rhs.count();
}
pair<bitset<100>,bitset<100> > a,b;
int main()
{
bitset<100> l(10),r(20);
cout<<(l<r)<<endl;
a=make_pair(r,r);
b=make_pair(l,l);
cout<<(a<b)<<endl;
}

代码中编译出错是因为你定义了 operator<(bistset<N> &, bitset<N> &) 然而没有定义 operator<(pair<...> &, pair<...> &).
你的第一段测试代码

namespace test{
class A{//means bitset<>
public:
int aa;
};
template <class T>
class B{//means pair<>
public:
T BT;
};

template <class T>
bool operator<(B<T> a,B<T> b)
{
return a.BT<b.BT; // 其实编译报错的问题在这里
}
}


bool operator<(test::A a,test::A b)
{
return a.aa<b.aa;
}

int main()
{
test::B<test::A> B1,B2;
B1<B2; // 这里调用了实例化的 bool operator<(B<T> a,B<T> b)
return 0;
}

但是在 bool operator<(B<T> a,B<T> b) 中你写了
return a.BT<b.BT;

这就是错误的根源
因为没有定义 operator<(A<...> &, A<...> &) 的函数.

你的第二段测试代码:

namespace test{
class A{//means bitset
public:
int aa;
};
template <class T>
class B{//means pair
public:
T BT;
};
template <class T>
bool operator<(B<T> a,B<T> b)
{
return a.BT<b.BT;
}
}


bool operator<(test::A a,test::A b)
{
return a.aa<b.aa;
}
//HERE!!!
bool operator<(test::B<test::A> a,test::B<test::A> b)
{
return a.BT<b.BT;
}

int main()
{
test::B<test::A> B1,B2;
B1<B2; // 这里显然解决了你上个例子的缺少的 operator<(A<> &, A<> &) 的问题. 因此通过编译
return 0;
}

luojc714 2008-12-04
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 bill830711 的回复:]
如果程序中对两个bitset变量比较大小,没有问题,但我比较两个
std::pair <bitset <N>,bitset <N> >类型的变量时,就会提示没有可匹配的operator <,
那位大牛给指点一下原因是什么?先谢过了:)


bool operator <(const std::bitset <_N> &lhs,const std::bitset <_N> &rhs)
因为你的模板函数写死了两个参数是 std::bitset <_N> 类型的,那么这个时候,_N 是一个size_t 类型的,并且可以随意变化, 但std::bitset 却…
[/Quote]
对...顶!

Jinhao 2008-12-04
  • 打赏
  • 举报
回复
嘿嘿,SORRY,我错误的回答把大家引入歧途..
visame 2008-12-04
  • 打赏
  • 举报
回复
瀑布汗。。。
真是羞愧难当啊!概念严重混淆。
是啊,我的第三段代码根本就没有出现过特化。那叫“重载”。。。
幸亏陶大妈一针见血的指出问题所在。
Jinhao 2008-12-04
  • 打赏
  • 举报
回复
我把lz的问题看错了...“特化一定要放在 原模板所在的域中”这句话是正确的.只是不应该用来回答lz的问题,lz的问题不是这个.
shailen126 2008-12-04
  • 打赏
  • 举报
回复
学习兼接分。。
hhyttppd 2008-12-04
  • 打赏
  • 举报
回复
另外我也发现“特化一定要放在 原模板所在的域中”也是正确的,建议楼主试一试。
tianshangfei 2008-12-04
  • 打赏
  • 举报
回复
学习~
wgzymzx 2008-12-04
  • 打赏
  • 举报
回复
大哥,看看C++的运算符重载的原理和机制,你就明白啦
taodm 2008-12-04
  • 打赏
  • 举报
回复
混淆了特化和普通函数是很多人容易犯的错,尤其会搞错它们在重载判决时的规则。
hhyttppd 2008-12-04
  • 打赏
  • 举报
回复
[Quote=引用 24 楼 taodm 的回复:]
汗,你这代码里根本就没有“特化”行为。
[/Quote]

呵呵,我也发现了。
bfhtian 2008-12-04
  • 打赏
  • 举报
回复
up
taodm 2008-12-04
  • 打赏
  • 举报
回复
汗,你这代码里根本就没有“特化”行为。
hhyttppd 2008-12-04
  • 打赏
  • 举报
回复
我犯的错误:
1 名字查找会考虑全局空间,只是要求在使用之前可见(因此实验一中的bool operator<(test::A a,test::A b)不会被做为候选函数)。
2 重载解析发生在名字查找之后。

关于:
2.我不同意10楼的"特化一定要放在 原模板所在的域中. "事实上我的最后一个例子已经说明不在一个域中也是可以的。对pair的operator <,一个在std中,特化的那个则在global scope中。
不知道您是否同意我的观点?

你的实验二可以通过编译,是因为test中的
template <class T>
bool operator<(B<T> a,B<T> b)
没有实例化过,一旦需要实例化你的函数,则会发生实验一的问题。

2.我不同意10楼的"特化一定要放在 原模板所在的域中. "事实上我的最后一个例子已经说明不在一个域中也是可以的。对pair的operator <,一个在std中,特化的那个则在global scope中。
不知道您是否同意我的观点?

特化是可以在全局空间中的,但是根据实验一,有可能不被特化定义点之前的代码使用,造成疑惑。
如:

namespace test{
class A{//means bitset
public:
int aa;
};
template <class T>
class B{//means pair
public:
T BT;
};
template <class T>
bool operator<(B<T> a,B<T> b)
{
return a.BT<b.BT;
}

}

#if 0
//在此处则不能使用
void test_name_resolver()
{
test::B<test::A> B1,B2;
B1<B2;
}

bool operator<(test::A a,test::A b)
{
return a.aa<b.aa;
}
//HERE!!!
bool operator<(test::B<test::A> a,test::B<test::A> b)
{
return a.BT<b.BT;
}

int main()
{
//此处可以使用
test::B<test::A> B1,B2;
B1<B2;
return 0;
}

#else

//在此处可以使用
void test_name_resolver()
{
test::B<test::A> B1,B2;
B1<B2;
}

namespace test{
bool operator<(test::A a,test::A b)
{
return a.aa<b.aa;
}

//HERE!!!
bool operator<(test::B<test::A> a,test::B<test::A> b)
{
return a.BT<b.BT;
}

}

int main()
{
//此处也可以使用
test::B<test::A> B1,B2;
B1<B2;
return 0;
}

#endif
Jinhao 2008-12-04
  • 打赏
  • 举报
回复
因为bitset没有operator<
所以在pair的operator<里,无法找到bitset::operator<的声明,你给出的定义在pair定义之后.
把这个operator<放在namespace std中,就能通过argument-dependent name lookup查找到.
Jinhao 2008-12-04
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 visame 的回复:]
同时,我与您还有一点不一致的地方:您在7楼所说的:“可以完全匹配从而停止继续查找,因此不会再使用koeing查找namespace test中的 ”
我认为不会“停止继续查找”。只是会在两者中间选择一个最佳的。于是选中了那个。根据C++ Primer, overload resolution的第一步就是找到所有的candidates。于是,我认为koeing查找是依然会进行的。
[/Quote]

恩,会继续argument-dependent name lookup,test::operator<是模板函数,不是最优匹配.

taodm 2008-12-04
  • 打赏
  • 举报
回复
C++2003标准:
17.4.3.1 Reserved names
It is undefined for a C + + program to add declarations or definitions to namespace std or namespaces
within namespace std unless otherwise specified. A program may add template specializations for any
standard library template to namespace std. Such a specialization (complete or partial) of a standard
library template results in undefined behavior unless the declaration depends on a user-defined name of
external linkage and unless the specialization meets the standard library requirements for the original template.
163)

163) Any library code that instantiates other library templates must be prepared to work adequately with any user-supplied specialization
that meets the minimum requirements of the Standard.
yshuise 2008-12-04
  • 打赏
  • 举报
回复
我以前也遇到过这个问题,搞了很久才发现:
这儿需要写成成员函数,你仔细看编译错误的信息就知道了。根本就没有调用你自己写的函数,
你自己打个断点,看进了没有,ok?
Jinhao 2008-12-04
  • 打赏
  • 举报
回复
"特化一定要放在 原模板所在的域中. "这句话没错, 我把问题看错了,没有看你的试验代码, 后来才注意问题是处在bitset上,取消上面的回答.

taodm 2008-12-04
  • 打赏
  • 举报
回复
“对namespace std进行修改是unspecified”是不准确的,教条主义的。
例如stl为了高效必须用户提供type_traits特化。具体见于《stl源码剖析》325。
加载更多回复(16)

65,206

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧