Почему я должен квалифицировать пространство имен для функции, переданной typedef для параметра типа функции templated? - PullRequest
0 голосов
/ 22 апреля 2020

В этом примере, почему мне нужно квалифицировать вызов функции B :: F () с ее пространством имен? Поскольку typedef QF должен соответствовать типу Q, как я думал, это псевдоним для него, а не новый тип?

Есть ли элегантный способ обойти это, не требующий использования оператора в каждой функции тело?

namespace A
{
    template<class T> struct V {};
    template<class T> struct Q {};


    typedef Q<float> QF;


    template<class T>
    inline void F(V<T> v) {}



    namespace B
    {
        template<class T>
        inline void F(Q<T> q) {}
    }


    namespace C
    {
        void Test();
    }
}


using namespace A;
using namespace A::B;


void A::C::Test()
{
    QF q;


    //  using namespace A::B;   //  Uncomment will work


    F(q);       //  Error, tries A::F() and can't find B::F()
    B::F(q);     //  Ok it uses B::F() as intended
}


int main()
{
    return 0;
}

1 Ответ

1 голос
/ 23 апреля 2020

Я попытаюсь показать, каким образом clang интерпретирует черновик иначе, чем g cc и msv c. Когда вы вызываете F(q) внутри определения void A::C::Test(), компилятор пытается найти имя F в областях, в которых выполняется этот вызов, и прекращает поиск больших областей, как только это имя (имя Особых перегрузок не обнаружено. Тело void A::C::Test() находится внутри пространства имен C, которое находится внутри A, которое находится внутри глобального пространства имен. Поэтому, когда вы пишете директиву using в глобальной области видимости, соответствующий отрывок в стандарте [basi c .lookup.unqual]:

  1. Во всех случаях, перечисленных в [basi c .lookup.unqual], области поиска объявлений в порядке, указанном в каждой из соответствующих категорий; поиск имени заканчивается, как только для имени найдено объявление. Если декларация не найдена, программа некорректна.
  2. Объявления из пространства имен, назначенного директивой using, становятся видимыми в пространстве имен, включающем директиву using; см. [namespace.udir]. Для целей правил поиска без оговорок, описанных в [basi c .lookup.unqual], объявления из пространства имен, назначенного директивой using, считаются членами этого вмещающего пространства имен.

по пункту 2 подразумевает, что имя F (от A::B::F) отображается так, как если бы оно было объявлено в глобальной области видимости, но при поиске имени по пункту 1 имя F быстрее найдется в пространстве имен A и дальше смотреть не буду. Таким образом, он находит только void F(V<T> v), и это объявление не подходит для вызова.

Более интересная история с директивой using, помещающей внутри тела функции. Разница в подходе заключается в том, появляется ли определение void A::C::Test() (которое также является объявлением) в глобальном пространстве имен или в пространстве имен A::C. Если вы придерживаетесь мнения, что это глобальное пространство имен (как, очевидно, и делает clang), то пространство имен, содержащее директиву using, написанную внутри функции, является глобальным пространством имен, и мы возвращаемся к объяснению выше. Но если вы верите, что оно появляется в пространстве имен A::C, то вы вносите (среди прочего) объявление void F(Q<T>) в A::C и регулярный поиск находит его.

Наблюдение за бонусом - это то, почему вы все равно сможете звонить F(V<double>{}), поскольку теперь объявление void F(Q<T>) находится в более узкой области видимости, а ответом является поиск, зависящий от аргумента, пространство имен, где V<T> - это Заявление будет объявлено тоже.

Это история, которая пугает ученика, но если вам нужно практическое руководство, не используйте ярлыки с пространствами имен, используйте

namespace A::C 
{
void Test()
{
...
}
}

, чтобы пространство имен, в котором оно объявлено, не подлежит обсуждению и помещается с использованием пространства имен A :: B; либо в области имен или в области функций. (И я постараюсь выяснить, какова была реальная цель определения декларации void A::C::Test() с более мудрыми людьми).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...