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

GCC与Clang对三元运算符模板代码的编译哪个符合C++标准?

哪个编译器的实现符合C++标准?

首先看问题中的代码:

#include <unordered_map>
#include <iostream>

template<template<class, class, class...> class Map, typename Key, typename T, typename KeyP, typename... Args>
const T& getMapEntry(const Map<Key, T, Args...>& map, const KeyP& key, const T& defval) {
    static_assert(std::is_convertible_v<KeyP, Key>);
    auto i = map.find(key);
    return i != map.end() ? i->second : defval;
}

template<template<class, class, class...> class Map, typename Key, typename T, typename KeyP, typename... Args>
T getMapEntry(const Map<Key, T, Args...>& map, const KeyP& key, T&& defval) {
    static_assert(std::is_convertible_v<KeyP, Key>);
    auto i = map.find(key);
    return i != map.end() ? i->second : std::move(defval);
}

template<template<class, class, class...> class Map, typename Key, typename T, typename KeyP, typename... Args>
T getMapEntry(const Map<Key, T, Args...>& map, const KeyP& key, const T&& defval) = delete;

class Foo {
public:
    Foo() { std::cout << __PRETTY_FUNCTION__ << '\n'; }
    ~Foo() { std::cout << __PRETTY_FUNCTION__ << '\n'; }
    Foo(const Foo&) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
    Foo& operator=(const Foo&) { std::cout << __PRETTY_FUNCTION__ << '\n'; return *this; }
    Foo(Foo&&) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
    Foo& operator=(Foo&&) { std::cout << __PRETTY_FUNCTION__ << '\n'; return *this; }
};

int main() {
    std::unordered_map<int, Foo> map;
    auto&& ret = getMapEntry(map, 123U, Foo{});
    return 0;
}

编译器输出

  • GCC输出:
Foo::Foo()
Foo::Foo(Foo&&)
Foo::~Foo()
Foo::~Foo()
  • Clang输出:
Foo::Foo()
Foo::Foo(const Foo &)
Foo::~Foo()
Foo::~Foo()

问题分析

首先明确:main中的map是空的,因此条件表达式会选择std::move(defval)分支。关键在于条件表达式的类型和值类别推导:

  1. 分支类型:

    • i->second是const Foo&(左值,因为函数参数map是const引用,迭代器为const_iterator)
    • std::move(defval)是Foo&&(xvalue,defval是Foo&&类型,std::move后仍为右值引用)
  2. C++标准中的条件表达式转换规则:
    当条件表达式的两个分支类型不同时,会尝试将其中一个分支转换为另一个分支的类型:

    • 先尝试将i->second(const Foo左值)转换为std::move(defval)的类型(Foo&&):不成立,因为非const右值引用无法绑定到const左值。
    • 再尝试将std::move(defval)(Foo&&)转换为i->second的类型(const Foo&):成立,右值允许绑定到const左值引用。

因此,条件表达式的最终结果类型是const Foo&,值类别为左值。当函数返回类型为Foo时,必须从const Foo&拷贝构造返回值,这与Clang的行为一致。

而GCC错误地将条件表达式的结果处理为Foo&&,导致调用移动构造函数,不符合C++标准。

补充说明

  • 使用if/else分支时,走else分支直接返回std::move(defval),此时返回语句会将Foo&&转换为Foo,调用移动构造函数,这和条件表达式的规则无关。
  • 若修改返回语句为return i != map.end() ? T{i->second} : std::move(defval);,T{i->second}是Foo类型的prvalue,与std::move(defval)的Foo&&共同构成的条件表达式结果为Foo类型的prvalue,返回时会触发移动构造(或拷贝消除),因此调用移动构造函数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 18:47:02