Этот вопрос несколько связан с точкой создания экземпляра и привязкой имени, но не совсем. Вопрос касается стандарта и того, как он разрешает поиск символов внутри определений шаблонов.
Рассмотрим этот пример, основанный на библиотеке 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
модуль), который использует модуль вывода для создания элементов.
Это позволяет определять базовые операторы emit внутри класса 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 стандарта С++ говорится (слегка отредактировано) :
[...] В выражении вида:
постфиксное-выражение
(
список-выражений opt)
где постфиксное-выражение является идентификатором, идентификатор обозначает зависимое имя тогда и только тогда, когда любое из выражений в списке-выражений — выражение, зависящее от типа (14.6.2.2).
И далее, в 14.6.2.2 («Выражения, зависящие от типа») §2 сказано:
this
зависит от типа, если тип класса включающей функции-члена зависит
Что, AIUI, имеет место здесь.
Итак, вопросы:
- Почему поиск не учитывает версию верхнего уровня
emit
в разрешении имени здесь? - Можно ли заставить второй пример работать так же, как и первый, чтобы определение функции-члена шаблона учитывало как функции-члены, так и функции области видимости пространства имен в момент создания экземпляра?
Update: Изменил название поста на более точное и сделал небольшую правку цитат из стандарта.
namespace adlenabler { void emit(); };
(делегируйте это в свой класс.. мне лень это показывать :)) и вместо этого напишитеusing adlenabler::emit; emit(*this, x);
- person Johannes Schaub - litb   schedule 15.11.2015Output
и функциюemit
в отдельное пространство имен и добавилusing
в функцию-член шаблона. Если вы добавите ответ здесь, я отмечу его для вас. - person Mats Kindahl   schedule 15.11.2015