2014-01-04 1 views
5

Недавно я изучил точное значение известного «двухфазного поиска имени» для имен в классах шаблонов. Хотя я прочитал много статей об этом, я все еще не могу все знать об этом. Теперь я запутался в коде, показанном ниже:Странное поведение при поиске зависимого от аргумента имени в шаблоне

template<typename T> 
class A 
{ 
public: 
    void f(T, T){}; 
}; 

namespace ns 
{ 
    typedef int TT; 
    void f(int, int){}; 
}; 

template<typename T> 
class B : public A<T> 
{ 
public: 
    void g() 
    { 

     //f(T(), T()); // it's fine for error here 
     typedef ns::TT TTT; 
     f(TTT(), T()); // why this issued an error? 
     f(ns::TT(), T()); // and this? 
    } 
}; 

/* I also think it's OK to move ns here */ 
// namespace ns 
// { 
// typedef int TT; 
// void f(int, int){}; 
//}; 

int main() 
{ 
    B<int> b; 
    b.g(); 
} 

Обратите внимание на второй комментарий. Поскольку «f» является зависимым именем, его поиск должен задерживаться до момента создания в «основной» функции. И в это время компилятор должен выполнить зависящий от аргумента поиск имени в области основной функции. Я думаю, что теперь он должен открыть эту функцию в пространстве имен нс, но она по-прежнему выдается ошибка компиляции:

1.cpp: In instantiation of 'void B<T>::g() [with T = int]': 
1.cpp:30:6: required from here 
1.cpp:23:15: error: 'f' was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive] f(TTT(), T()); //why this issued an error? 
      ^
1.cpp:23:15: note: declarations in dependent base 'A<int>' are not found by unqualified lookup 
1.cpp:23:15: note: use 'this->f' instead 

Может кто-нибудь объяснить мне это? Благодарю.

+2

[basic.lookup.argdep]/2 «для каждого типа аргумента' T' в вызове функции, существует набор из нуля или более связанных с пространствами имен, и множество нулевого или более связанных классов. Множества пространств имен и классов определяются полностью типами аргументов функции (и пространства имен любого аргумента шаблона шаблона). ** Названия Typedef и используемые декларации, используемые для указания типы не вносят свой вклад в этот набор. ** «[акцент мой] Я не думаю, что эта проблема связана с шаблонами. – dyp

+0

А как насчет ошибки при третьем комментарии? f (ns :: TT(), T())? – sunlight07

+0

Ну 'ns :: TT' - это typedef для' int'. И 'ns' не является ассоциированным пространством имен' int'. Это же правило применяется. (Присутствие 'ns ::' просто изменяет поиск имени для 'TT', следуя за' :: ', это не изменяет поиск имени' f' - это все еще зависит только от типов аргументов.) – dyp

ответ

3

Поиск, зависящий от аргументов, выполняет поиск только в связанных классах и пространствах имен типа аргумента. Typedef - это просто прозрачный псевдоним, аналогично использование-объявления.

Из проекта стандарта n3485, [basic.lookup.argdep]/2 об аргумента-зависимого поиска:

For each argument type T in the function call, there is a set of zero or more associated namespaces and a set of zero or more associated classes to be considered. The sets of namespaces and classes is determined entirely by the types of the function arguments (and the namespace of any template template argument). Typedef names and using-declarations used to specify the types do not contribute to this set.

[курсив мой]


template<typename T> 
class A 
{ 
public: 
    void f(T, T){}; 
}; 

namespace ns 
{ 
    typedef int TT; 
    void f(int, int){}; 
}; 

template<typename T> 
class B : public A<T> // note the base class is dependent 
{ 
public: 
    void g() 
    { 
     //f(T(), T()); // it's fine for error here 
     typedef ns::TT TTT; 
     f(TTT(), T()); // why this issued an error? 
     f(ns::TT(), T()); // and this? 
    } 
}; 

В качестве базового класса зависит от него, он не будет искать при неквалифицированном поиске. Поэтому f можно найти, используя зависящий от аргумента поиск. Вы правильно указали, что f будет искать только во время «второй фазы» (в момент создания), как и в ваших вызовах, по крайней мере один аргумент зависит от параметра шаблона.

Однако nsэто не зависимое имя, а также TTns::TT). Поэтому пространство имен и TT должны быть объявлены до того, как они будут использованы в определении g.

ли вы написать f(ns::TT(), T()) или f(T(), T()) не влияет на общее правило, где f ищется во время аргумента в зависимости от поиска: Только связанные пространства имен и классы типов аргументов (из T и ns::TT). Оба значения: int s для B<int>::g(), поэтому нет связанных классов и пространств имен.

f(TTT(), TTT()) поиск изменений постольку, поскольку f теперь ищет в фазе поиска первого имени (это не зависимое имя).


Вот пример аргумента-зависимого поиска:

namespace ns 
{ 
    struct TT {}; 
    void f(TT, TT) {} 
} 

int main() 
{ 
    ns::TT x; 
    f(x, x); 
} 

Теперь вы можете сделать это, а внутри функции-члена шаблона класса:

namespace ns 
{ 
    struct TT {}; 
    void f(TT, TT) {} 
} 

template<typename T> 
struct B 
{ 
    void g() 
    { 
     f(T(), T()); 
    } 
}; 

int main() 
{ 
    B<ns::TT> x; 
    x.g(); 
} 

Но, как Я сказал, что зависящий от аргумента поиск имени не работает для основных типов, таких как int.

В приведенном выше примере f снова зависит, поскольку по крайней мере один из аргументов зависит от параметра шаблона. Таким образом, можно записать так:

template<typename T> 
struct B 
{ 
    void g() 
    { 
     f(T(), T()); // looked up during the second phase, 
        // from the point of instantiation 
    } 
}; 

namespace ns 
{ 
    struct TT {}; 
    void f(TT, TT) {} 
} 

int main() 
{ 
    B<ns::TT> x; 
    x.g(); 
} 
+0

Спасибо за ваше объяснение! Еще один вопрос, если я разделил пространство имен ns на две части: struct TT определяется перед основной функцией, а f определяется после основной функции. Ошибка не возникает. Правильно ли это? Если я добавлю «ns :: TT y; f (y, y)»; после x.g() в основной функции и перемещения объявления, он говорит: «f не объявлен»? Но для шаблона все в порядке? – sunlight07

+0

@ sunlight07 В этом разница между поиском зависимых имен внутри шаблонов и имен за пределами шаблонов: 'f' в' f (T(), T()) 'является зависимым именем и ищет« во время второй фазы », из точка инстанцирования. На самом деле, есть несколько таких точек, и один находится в конце файла. Таким образом, можно найти объявление 'f', которое после определения' g' и даже после вызова 'g' в' main'. Тем не менее, IIRC вам нужно быть осторожным с этим (объявление после вызова). – dyp

+0

Практически (странно, что никто не упомянул об этом), локальный 'using ns :: f;' сделал бы чудеса, я думаю. –

Смежные вопросы