You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用typedef定义带自定义比较器的set跨文件调用时出现编译链接错误

问题

尝试用typedef定义带自定义比较器的std::set,跨文件使用时出现编译链接错误。简化后的代码如下:

A.hpp

#ifndef A_HEADER
#define A_HEADER

#include <set>

struct Tuple {
    int i;
    char c;
};

auto cmp = [](const Tuple &a, const Tuple &b) { return a.i < b.i; };
typedef std::set<Tuple, decltype(cmp)> TupleSet;

TupleSet getTuples(void);

#endif

A.cpp

#include "A.hpp"

TupleSet getTuples(void) {
    TupleSet s(cmp);
    for (int i = 0; i < 5; ++i) {
        Tuple t = {i, 'a'};
        s.insert(t);
    }

    return s;
}

B.hpp

#ifndef B_HEADER
#define B_HEADER

#include "A.hpp"

void foo(void);

#endif

B.cpp

#include "B.hpp"
#include <iostream>

void foo(void) {
    TupleSet s = getTuples();
    for (const Tuple &t : s) {
        std::cout << "(" << t.i << ", " << t.c << ")\n";
    }
}

int main(void) {
    foo();
}

编译命令:

clang++ -c -o A.o A.cpp
clang++ -o B B.cpp A.o

出现的错误:

In file included from B.cpp:1:
In file included from ./B.hpp:4:
./A.hpp:15:10: warning: function 'getTuples' has internal linkage but is not defined [-Wundefined-internal]
TupleSet getTuples(void);
^
B.cpp:5:18: note: used here
TupleSet s = getTuples();
^
1 warning generated.
/usr/bin/ld: /tmp/B-ec93ec.o: in function foo()': B.cpp:(.text+0x10): undefined reference to getTuples()'
clang: error: linker command failed with exit code 1 (use -v to see invocation)

问题原因与解决方法

核心原因

头文件A.hpp中,auto cmp = []{...};定义的lambda是内部链接变量——头文件被多个cpp包含时,每个编译单元都会生成独立的cmp实例。而每个lambda的类型都是唯一的,哪怕代码完全一致,这就导致decltype(cmp)在A.cpp和B.cpp中被推导为不同类型,最终让两个文件中getTuples的返回类型不匹配,链接器找不到对应实现。

解决方法

有三种可行的修复方式:

方式一:用函数对象类替代lambda

把比较逻辑封装成具名类,确保所有编译单元中类型一致:

// 修改A.hpp中的cmp和typedef
struct TupleCmp {
    bool operator()(const Tuple &a, const Tuple &b) const {
        return a.i < b.i;
    }
};
typedef std::set<Tuple, TupleCmp> TupleSet;

此时A.cpp中初始化set无需传参数,直接写TupleSet s;即可。

方式二:让lambda拥有外部链接

C++17及以上版本可将cmp声明为inline,保证所有编译单元共享同一个lambda实例,类型统一:

// 修改A.hpp中的cmp定义
inline auto cmp = [](const Tuple &a, const Tuple &b) { return a.i < b.i; };

这种方式保留lambda写法,同时解决类型不一致问题。

方式三:使用函数指针作为比较器

用普通函数替代lambda,以函数指针类型作为set的比较器:

// 修改A.hpp
bool cmp(const Tuple &a, const Tuple &b) {
    return a.i < b.i;
}
typedef std::set<Tuple, bool(*)(const Tuple&, const Tuple&)> TupleSet;

A.cpp中初始化set时需传入函数指针:TupleSet s(&cmp);,但这种方式性能略逊于函数对象(函数指针无法被内联)。

内容的提问来源于stack exchange,提问作者terrabyte

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 13:55:11