Функция шаблона C ++ не компилируется с g ++ при использовании пространства имен - PullRequest
0 голосов
/ 04 июня 2018

Следующий код компилируется просто отлично: (без пространства имен)

#include <vector>

template <class T>
void foo(const int & from, std::vector<T> & to)
{
    for (int i = 0; i < 5; i++)
    {
        T bar;
        foo(from, bar);
        to.push_back(bar);
    }
}

struct Bar
{
    int a;
    int b;
};

struct Baz
{
    std::vector<Bar> bars;
};


void foo(const int & from, Bar & to)
{
    to.a = from;
    to.b = from - 1;
}

void foo(const int & from, Baz & to)
{
    foo(from, to.bars);
}

void fooTest()
{
    int num = 10;
    Baz baz;
    foo(num, baz);
}

int main()
{
    fooTest();
}

Но когда я представляю пространство имен для Bar и Baz, он не компилируется. (с пространством имен)

#include <vector>

template <class T>
void foo(const int & from, std::vector<T> & to)
{
    for (int i = 0; i < 5; i++)
    {
        T bar;
        foo(from, bar);
        to.push_back(bar);
    }
}

// When I add this namespace, it fails to compile
namespace BarBar
{
    struct Bar
    {
        int a;
        int b;
    };

    struct Baz
    {
        std::vector<Bar> bars;
    };
}


void foo(const int & from, BarBar::Bar & to)
{
    to.a = from;
    to.b = from - 1;
}

void foo(const int & from, BarBar::Baz & to)
{
    foo(from, to.bars);
}

void fooTest()
{
    int num = 10;
    BarBar::Baz baz;
    foo(num, baz);
}

int main()
{
    fooTest();
}

Отображается ошибка:

with_namespace.cpp: In instantiation of ‘void foo(const int&, std::vector<T>&) [with T = BarBar::Bar]’:
with_namespace.cpp:37:22:   required from here
with_namespace.cpp:9:12: error: no matching function for call to ‘foo(const int&, BarBar::Bar&)’
         foo(from, bar);
            ^
with_namespace.cpp:4:6: note: candidate: template<class T> void foo(const int&, std::vector<T>&)
 void foo(const int & from, std::vector<T> & to)
      ^
with_namespace.cpp:4:6: note:   template argument deduction/substitution failed:
with_namespace.cpp:9:12: note:   ‘BarBar::Bar’ is not derived from ‘std::vector<T>’
         foo(from, bar);
        ^

Также обратите внимание, что код с пространством имен компилируется очень хорошо при использовании MSVC.Почему компилятор не может найти определение при использовании пространства имен?

Я использую следующую версию: g ++ (Ubuntu 5.4.0-6ubuntu1 ~ 16.04.9) 5.4.0 20160609

ОБНОВЛЕНИЕ: После того, как @MM указал на то, как функция поиска работает для шаблонов и ADL, я пошел со следующим исправлением:

#include <vector>



template <class T>
void foo(const int & from, std::vector<T> & to)
{
    for (int i = 0; i < 5; i++)
    {
        T bar;
        foo(from, bar);
        to.push_back(bar);
    }
}

namespace BarBar
{
    struct Bar
    {
        int a;
        int b;
    };

    struct Baz
    {
        std::vector<Bar> bars;
    };
};


// Put them in the same namespace as Bar so that the templated foo find this function
namespace BarBar
{
    using ::foo; // We are going to use templated foo in the latter functions

    void foo(const int & from, BarBar::Bar & to)
    {
        to.a = from;
        to.b = from - 1;
    }

    void foo(const int & from, BarBar::Baz & to)
    {
        foo(from, to.bars);
    }

}

void fooTest()
{
    int num = 10;
    BarBar::Baz baz;
    BarBar::foo(num, baz);
}



int main()
{
    fooTest();
}

Ответы [ 2 ]

0 голосов
/ 04 июня 2018

В коде:

template <class T>
void foo(const int & from, std::vector<T> & to)
{
    T bar;
    foo(from, bar);

имя bar зависит от типа , поскольку его тип зависит от параметра шаблона.Кроме того, имя foofoo(from, bar)) является зависимым именем , поскольку один из аргументов вызова функции зависит от типа.(C ++ 17 [temp.dep] / 1).

Поиск имен для зависимых имен работает следующим образом (C ++ 17 [temp.dep.res] / 1):

При разрешении зависимых имен учитываются имена из следующих источников:

  • Объявления, видимые в точке определения шаблона.
  • Объявления из пространств имен, связанных стипы аргументов функции как из контекста экземпляра, так и из контекста определения

Вторая точка маркера известна как ADL (поиск, зависящий от аргумента).* Во втором коде поиск зависимого foo ничего не находит:

  • нет другого определения, видимого в точке шаблона
  • Связанные пространства имен intи T (то есть BarBar::Bar): BarBar, и имя отсутствует BarBar::foo.

В первом коде ищем зависимый foo: связанные пространства именint и ::Bar: глобальное пространство имен.В глобальном пространстве имен есть ::foo, так что он найден ADL.

Чтобы исправить второй код, вы должны переместить более поздние определения foo, которые принимают аргумент BarBar::, чтобы быть внутри namespace BarBar.(Вам также понадобится using ::foo в строке 37, чтобы найти шаблон foo в этом случае).

0 голосов
/ 04 июня 2018

Ошибка в шаблоне foo():

foo(from, bar);
       // ^^^ error

Точка объявления всех имен в пространстве имен начинается с идентификатора спецификатора пространства имен.

Ошибка связана стот факт, что BarBar::Bar еще не было определено до вашей шаблонной функции foo.

Вот почему предварительное объявление решает проблему.

...