Този въпрос е донякъде свързан с точката на инстанциране и обвързването на име, но не точно. Въпросът е за стандарта и как разрешава търсенето на символи в дефинициите на шаблона.
Помислете за този пример, базиран на библиотеката 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
, но всеки модул, който дефинира нови класове и иска да предостави метод за излъчване, може да го направи, като предостави функция приятел с два аргумента. Всичко наред досега.
Сега нека се опитаме да използваме същата идея без претоварване на оператора и вместо това да използваме функции на член на плана за предварително дефинираните излъчващи функции за базовия тип и все пак да позволим на специфичните за класа излъчващи функции да бъдат дефинирани като приятелски функции за класа:
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++ казва (леко редактиран) :
[...] В израз на формата:
постфикс-израз
(
списък с изрази opt)
където постфиксният-израз е идентификатор, идентификаторът обозначава зависимо име ако и само ако някой от изразите в списък-изрази е зависим от типа израз (14.6.2.2).
И по-нататък, в 14.6.2.2 ("Изрази, зависими от типа") §2 се казва:
this
е зависим от типа, ако типът на класа на обхващащата функция член е зависим
Което, AIUI, е случаят тук.
И така, въпросите са:
- Защо търсенето не взема предвид версията от най-високо ниво на
emit
в резолюцията на име тук? - Възможно ли е вторият пример да работи по същия начин като първия, така че дефиницията на функция член на шаблона да вземе предвид както функциите член, така и функциите на обхвата на пространството от имена в точката на инстанциране?
Актуализация: Променено е заглавието на публикацията, за да бъде по-точно и е направена лека редакция на цитатите от стандарта.
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