【发布时间】:2015-11-15 16:04:39
【问题描述】:
这个问题与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 运算符,但是任何定义新类并想要提供 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 不是从属名称,因此在定义点(模板的)而不是实例化点进行查找,而是在 C++ 标准中的第 14.6.2 节第 1 节中查找说(略有编辑):
[...] 形式的表达式:
后缀表达式
(表达式列表 opt)其中后缀表达式是一个标识符标识符表示一个依赖名当且仅当有expression-list 中的表达式是依赖于类型的表达式 (14.6.2.2)。
此外,在 14.6.2.2(“类型相关表达式”)§2 中它说:
this是类型相关的,如果封闭的成员函数的类类型是相关的
AIUI 就是这种情况。
所以,问题是:
- 为什么在此处的名称解析中查找不考虑
emit的顶级版本? - 是否可以使第二个示例以与第一个示例相同的方式工作,以便模板成员函数定义在实例化时同时考虑成员函数或命名空间范围函数?
更新:将帖子的标题更改为更准确,并对标准中的引用进行了轻微编辑。
【问题讨论】:
-
因为如果找到成员函数(emit 是成员),ADL 就不会发生。在实例化点发生的唯一事情是 ADL 查找。让它找到一个非成员函数(查找“使用 std::swap;”成语)。
namespace adlenabler { void emit(); };(代表你的班级..我懒得展示:))而是写using adlenabler::emit; emit(*this, x); -
嗯?您知道所描述的标准在哪里吗?
-
啊忘了我说的关于委派给成员的内容。无论如何,这不是故意的,而且它们的参数不足。好吧,我说的其他事情仍然适用:)
-
我设法使用您的建议使其工作。我将
Output类和emit函数放在单独的命名空间中,并将using添加到模板成员函数中。如果您在此处添加答案,我会为您打勾。 -
我会发布一个答案来澄清情况。仍然缺少使用声明的段落来解释它的工作原理。
标签: c++ templates name-lookup