C ++ 11 Lambdas和Templates会导致奇怪的链接器行为

时间:2012-06-07 20:42:09

标签: c++ templates c++11 g++ multiple-definition-error

在我的代码中,我有包含lambda表达式的函数模板,这些表达式取决于一些模板参数。最近我收到了链接器错误,可能是由于我的g ++编译器的更新,但不幸的是,我不确切知道。

我将举一个演示该问题的小例子。因为它是一个链接器问题,我们必须创建几个文件来演示它。我们有common.hpp,其中包含一个公共模板函数,两个模块a.cpp / a.hppb.cpp / b.hpp使用该函数和{{1包含main.cpp函数的模块。

main

档案// common.hpp #include <algorithm> template <class Iterator, typename Iterator::value_type x> void my_transform(Iterator begin, Iterator end) { std::transform(begin, end, begin, [] (typename Iterator::value_type y) { return x+y; }); }

a.cpp

档案// a.cpp #include "common.hpp" #include "a.hpp" void a(std::vector<int>& vec) { my_transform<std::vector<int>::iterator, 5>(vec.begin(), vec.end()); }

a.hpp

档案#include <vector> void a(std::vector<int>& vec);

b.cpp

档案// b.cpp #include "common.hpp" #include "b.hpp" void b(std::vector<int>& vec) { my_transform<std::vector<int>::iterator, 5>(vec.begin(), vec.end()); }

b.hpp

档案#include <vector> void b(std::vector<int>& vec);

main.cpp

如果我使用

进行编译和链接
int main() { return 0; }

我收到g++-4.7 -std=c++11 -c a.cpp g++-4.7 -std=c++11 -c b.cpp g++-4.7 -std=c++11 -c main.cpp g++ a.o b.o main.o 错误:

multiple-definition

基本上它表示lambda表达式已在a中定义。好的。如果我将b.cpp:(.text+0x30): multiple definition of `void my_transform<__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, 17>(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >)::{lambda(int)#1}::operator int (*)(int)() const' a.o:a.cpp:(.text+0x30): first defined here 中的模板参数从5更改为7,则一切正常。

问题:

  1. 这是我应该期待的还是b中的错误?我非常确定我使用早期版本的g++
  2. 的debian软件包编译了这段代码
  3. 除了不使用lambdas之外还有其他解决方法吗?如果结果符号是静态的,那么就不会有任何问题,例如,我认为。 更新:解决方法:设置my_transform g++-4.7static
  4. 这个问题不是很重要。 “不要在这里使用lambdas”方法没有问题,但我很好奇。 :)

2 个答案:

答案 0 :(得分:4)

这是g ++中的一个错误,它是在4.7.0-11和4.7.0-12之间引入的(我测试了这两个Debian版本)。 gcc-snapshot (20120601-1)也很好,不幸的是,我不知道它们之间有什么区别 - 4.7.0-12是6天后和另一个分支,我这里没有gcc存储库相比)。我在gcc的bugzilla中找不到相关条目。

标准的相关部分是

  

类类型(第9条),枚举类型(7.2),内联函数可以有多个定义   外部链接(7.1.2),类模板(第14章),非静态函数模板(14.5.6),静态数据成员   类模板(14.5.1.3),类模板的成员函数(14.5.1.1)或模板特化   其中一些模板参数未指定(14.7,14.5.5),在程序中提供了每个定义   出现在不同的翻译单元中,并且如果定义满足以下要求......那么程序应该表现得就像D [D是那个类/函数/其他] 的单个定义一样。

当您将此段落应用于my_transform时,您会发现它是一个非静态函数模板,它满足要求(为简洁起见省略),因此程序应该表现得好像只有一个定义整个计划。无论内在的是什么都存在,所以lambda的operator()是否为inline并不重要(它应该是,但无关紧要 1功能)。

BTW穷人的lambda函数等价物(实际上应该等同于AFAIK)

template <class Iterator, typename Iterator::value_type x>
void
my_transform(Iterator begin, Iterator end)
{
  struct Foo {
    auto operator()(typename Iterator::value_type y) const -> decltype(x+y) { return x+y; }
  };
  std::transform(begin, end, begin,
                 Foo());
}

仍然有效。

1:我不确定它本身是否会受到ODR的影响,因为它没有联系(参见5.1.2 / 3和3.5 / 8)

为了确保我不会忘记它,错误是由提交f899a730d4f41b6a20b5508059a450f3a9347316

引入的

答案 1 :(得分:1)

这看起来像编译器/链接器问题4.6.1没有它。

此命令在您的系统上输出什么?

nm -C a.o | grep 'lambda(int)#1'

使用4.6.1时,有一个弱符号(std::transform的实例化)和一个本地符号(lambda的operator())。根本没有定义operator int (*)(int)() const(似乎导致问题的原因)。