4

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

我将举一个小例子来说明这个问题。因为这是一个链接器问题,我们必须创建几个文件来演示它。我们有common.hpp,它包含一个通用模板函数,两个模块a.cpp/a.hppb.cpp/b.hpp使用该函数和一个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错误:

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

基本上它说 lambda 表达式已经在 a.xml 中定义。好的。如果我将模板参数b从 5 更改为 7,一切正常。

问题:

  1. 这是我应该期待的还是它的错误g++?我很确定我使用早期版本的 debian 包编译了这段代码g++-4.7
  2. 除了不使用 lambda 之外,还有其他解决方法吗?我认为,如果生成的符号是静态的,则不会有任何问题。更新:解决方法:制作 my_transformstaticinline.

这个问题不是很重要。“不要在这里使用 lambdas”方法没有问题,但我很好奇。:)

4

2 回答 2

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 is that class/function/whatever]的定义。

将此段应用于 时my_transform,您会看到它是一个非静态函数模板,它满足要求(为简洁起见,省略),因此程序应该表现得好像整个程序中只有一个定义。不管里面有什么,这都成立,所以operator()lambda 是否存在都无关紧要inline(应该是,但实际上并不重要1)。

顺便说一句,穷人的 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引入的

于 2012-06-07T22:26:34.803 回答
1

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

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

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

?

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

于 2012-06-07T21:39:36.363 回答