使用命名空间时,C ++模板函数无法使用g ++进行编译

时间:2018-06-04 02:05:31

标签: c++ templates gcc namespaces g++

以下代码编译得很好:(没有名称空间)

#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

更新 在@ M.M指出函数查找如何为模板和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 :(得分:1)

在代码中:

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

名称bar类型相关的,因为其类型取决于模板参数。此外,名称foo(在foo(from, bar)中)是依赖名称,因为其中一个函数调用参数是类型相关的。 (C ++ 17 [temp.dep] / 1)。

依赖名称的名称查找工作原理如下(C ++ 17 [temp.dep.res] / 1):

  

在解析依赖名称时,会考虑以下来源的名称:

     
      
  • 在模板定义时可见的声明。
  •   
  • 来自实例化上下文和定义上下文中与函数参数类型相关联的名称空间的声明
  •   

第二个项目符号点称为ADL(依赖于参数的查找)。

在第二段代码中,查找从属foo找不到任何内容:

  • 在模板
  • 处没有其他可见的定义
  • intTBarBar::Bar)的关联命名空间为:BarBar,且没有名称BarBar::foo

在第一个代码中,查找依赖的foo:int::Bar的关联命名空间是:全局命名空间。全局命名空间中有::foo,因此ADL可以找到它。

要修复第二个代码,您应该将后面带有foo参数的BarBar::定义移到namespace BarBar内。 (在这种情况下,您还需要第37行的using ::foo来找到模板foo。

答案 1 :(得分:0)

模板化foo()中的错误:

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

命名空间内所有名称的声明点从命名空间说明符id开始。

该错误是由于BarBar::Bar尚未定义之前您的模板化foo函数。

这就是前瞻性声明解决问题的原因。