函数存入std::pair的C++编译器差异及C++17标准合规性疑问
函数存入std::pair的C++17合规性探究
以下是涉及的示例代码:
void foo(int) {} // (1) GCC、Clang、MSVC均不允许 std::pair<decltype(foo), decltype(foo)&> myFirstPair; // (2) GCC可编译,Clang和MSVC编译失败 auto myPair = std::pair{foo, foo}; // (3) GCC、Clang、MSVC均可编译 auto myOtherPair = std::make_pair(foo, foo);
编译结果与核心问题
- (1) 所有编译器均拒绝:C++标准明确禁止类成员为函数类型,
decltype(foo)是函数类型void(int),直接声明该类型的std::pair成员违反规则。 - (3) 所有编译器均通过:
myOtherPair的类型被推导为std::pair<void (*)(int), void (*)(int)>,即存储函数指针而非函数类型。 - (2) 仅GCC通过:GCC将
myPair推导为函数指针类型的std::pair,但Clang和MSVC尝试推导函数类型,因类成员不能为函数类型而报错。
标准条款依据
C++17标准中[conv.func]条款规定了函数到指针的转换规则:
7.3 函数到指针的转换 [conv.func]
函数类型T的左值可转换为“指向T的指针”类型的纯右值,结果为指向该函数的指针。
编译器行为分析
三款编译器都会对std::make_pair的参数应用上述转换,因为std::make_pair的模板参数推导要求参数按值传递,函数左值必须转换为指针才能满足按值传递的要求。
但对于std::pair的列表初始化构造函数(即(2)的场景),标准并未明确要求在模板参数推导过程中必须自动应用函数到指针的转换。此时GCC选择主动应用转换,推导为合法的函数指针类型;而Clang和MSVC选择严格按照参数的原始类型推导,得到非法的函数类型,进而报错。
合规性结论
三款编译器的行为均未违反C++17标准:
- (1)的一致拒绝是符合标准的正确行为;
- (3)的一致通过也是符合标准的正确行为;
- (2)的差异属于标准未明确规定的模糊场景,编译器有权选择是否在该推导场景下应用函数到指针的转换,因此GCC、Clang和MSVC的实现均属合规。
内容的提问来源于stack exchange,提问作者Lukas Barth
相关产品推荐
相关产品推荐

