这个问题与point of instantiation and name binding有些相关,但并不完全相关。问题是标准及其如何解决模板定义中符号的查找。
考虑这个例子,松散地基于ostream库:
// Output module
class Output {
public:
void operator<<(int);
void operator<<(double);
...
};
// Item module
class Item {
friend void operator<<(Output& obj, const Item& x) {
...
}
};
// Main program
int main() {
Output out;
Item item;
out << 3;
out << 2.0;
out << item;
}
在这个例子中,关键点是输出模块是在任何使用它的模块之前定义的,并且有一个模块(Item
模块)使用输出模块发出项目。
这允许在Output
类中定义基本发射运算符,但是任何定义新类并且想要提供emit方法的模块都可以通过提供具有两个参数的友元函数来实现。到目前为止一切都很好。
现在,让我们尝试在没有运算符重载的情况下使用相同的想法,而是使用计划成员函数作为基类型的预定义发射函数,并且仍然允许将特定于类的发射函数定义为类的友元函数:
class Output {
public:
template <class Type>
void emit(Type x) {
emit(*this, x);
}
void emit(int);
void emit(double);
};
class Item {
friend void emit(Output& obj, const Item& x) {
...
}
...
};
int main() {
Output out;
Item item;
out.emit(3);
out.emit(2.0);
out.emit(item);
}
与前面的代码相比,添加了一个模板函数,因为根据类型不一定要有不同的调用约定。换句话说,无论发出什么项目,都应该可以使用约定out.emit(...)
。
但是,在编译时(使用GCC 4.8.4),我们会收到以下错误:
example.cc: In instantiation of ‘void Output::emit(Type) [with Type = Item]’:
example.cc:49:20: required from here
example.cc:33:9: error: no matching function for call to ‘Output::emit(Output&, Item&)’
emit(*this, x);
^
example.cc:33:9: note: candidates are:
example.cc:32:12: note: template<class Type> void Output::emit(Type)
void emit(Type x) {
^
example.cc:32:12: note: template argument deduction/substitution failed:
example.cc:33:9: note: candidate expects 1 argument, 2 provided
emit(*this, x);
^
example.cc:36:12: note: void Output::emit(int)
void emit(int) {
^
example.cc:36:12: note: candidate expects 1 argument, 2 provided
example.cc:37:12: note: void Output::emit(double)
void emit(double) {
^
example.cc:37:12: note: candidate expects 1 argument, 2 provided
换句话说,从不考虑顶级emit
函数,而是在解析名称时仅考虑Output
类中的成员函数。
我认为这是因为符号emit
不是依赖名称,因此被查找(模板的)定义点而不是实例化点,但是第14.6.2节§1中的C ++标准说(稍加编辑):
[...]表达形式:
postfix-expression
(
表达式列表 opt)
其中 postfix-expression 是标识符 标识符表示依赖名称当且仅当有 expression-list 中的表达式是一个依赖于类型的表达式(14.6.2.2)。
此外,在14.6.2.2(“类型依赖表达式”)§2中它说:
如果封闭成员函数的类类型依赖,则
this
依赖于类型
其中,AIUI就是这种情况。
所以,问题是:
emit
的顶级版本?更新:更改帖子的标题更准确,并对标准中的引号进行了轻微修改。
答案 0 :(得分:0)
如Johannes所述,如果找到成员函数,则不会调用ADL,并且需要ADL来查找Item
的朋友声明。
所以,回答第一个问题,C ++标准(C ++ 03版)中的第3.4.2节§2说:
如果名称的普通非限定查找找到类成员函数的声明,则不考虑关联的名称空间和类。 [...]
要回答第二个问题,这里是固定的示例代码:
namespace emitter {
class Output;
template <class Type>
void emit(Output& out, const Type& t) {
emit(out, t);
}
class Output {
public:
template <class Type>
void emit(Type x) {
using emitter::emit;
emit(*this, x);
}
void emit(int);
void emit(double);
};
}
class Item {
friend void emit(emitter::Output& obj, const Item& x) {
...
}
};
然而,我仍然在努力寻找澄清为什么using
声明解决问题的段落。我现在能找到的最接近的是7.3.3§13:
出于重载解析的目的, using-declaration 引入派生类的函数将被视为派生类的成员。
但这指的是using B::f
来自D
的{{1}},因此不是完美匹配。