This question is somewhat related to point of instantiation and name binding but not exactly. The question is about the standard and how it resolve lookup of symbols inside template definitions.
Consider this example, loosely based on ostream library:
// 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;
}
In this example, the key points is that the output module is defined before any modules that use it and there is one module (Item
module) that uses the output module to emit items.
This allow the base emit operators to be defined inside the Output
class, but any module that define new classes and want to provide an emit method can do so by providing a friend function with two arguments. All fine so far.
Now, let's try to use the same idea without operator overloading and instead use plan member functions for the pre-defined emit functions for the base type and still allow class-specific emit functions to be defined as friend functions for the class:
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);
}
Compared to the previous code, there is a template function added because it should not be necessary to have different calling conventions depending on the type. In other words, it should be possible to use the convention out.emit(...)
regardless of what item is being emitted.
However, when compiling this (using GCC 4.8.4), we get the following error:
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
In other words, the top-level emit
function is never considered and instead only the member functions inside Output
class is considered when resolving the name.
I assumed this was because the symbol emit
was not a dependent name and hence was looked up at the point of definition (of the template) instead of point of instantiation, but section 14.6.2 §1 in the C++ Standard says (slightly edited):
[...] In an expression of the form:
postfix-expression
(
expression-list opt)
where the postfix-expression is an identifier the identifier denotes a dependent name if and only if any of the expressions in expression-list is a type-dependent expression (14.6.2.2).
And further, in 14.6.2.2 ("Type-dependent expressions") §2 it says:
this
is type-dependent if the class type of the enclosing member function is dependent
Which, AIUI, is the case here.
So, the questions are:
emit
in the name resolution here?Update: Changed the title of the post to be more accurate and did a slight edit of the quotes from the standard.
As Johannes points out, if a member function is found ADL is not invoked and ADL is necessary to find the friend declaration of Item
.
So, to answer the first question, Section 3.4.2 §2 in the C++ standard (C++03 version) says:
If the ordinary unqualified lookup of the name finds the declaration of a class member function, the associated namespaces and classes are not considered. [...]
To answer the second question, here is the fixed sample code:
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) {
...
}
};
I am, however, still struggling to find the paragraph that clarifies why the using
declaration solves the issue. The closest I can find right now is 7.3.3 §13:
For the purpose of overload resolution, the functions which are introduced by a using-declaration into a derived class will be treated as through they were members of the derived class.
But this refers to using B::f
in a class D
derived from B
, so not a perfect match.