在我的代码中,我有包含lambda表达式的函数模板,这些表达式取决于一些模板参数。最近我收到了链接器错误,可能是由于我的g ++编译器的更新,但不幸的是,我不确切知道。
我将举一个演示该问题的小例子。因为它是一个链接器问题,我们必须创建几个文件来演示它。我们有common.hpp
,其中包含一个公共模板函数,两个模块a.cpp
/ a.hpp
和b.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,则一切正常。
问题:
b
中的错误?我非常确定我使用早期版本的g++
。g++-4.7
或static
。 这个问题不是很重要。 “不要在这里使用lambdas”方法没有问题,但我很好奇。 :)
答案 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
(似乎导致问题的原因)。