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

为何不同C++版本及编译器对用户定义右值引用转换的重载决议不同?

GCC与Clang在C++17下的重载决议差异问题

异常现象

当参数通过用户定义转换运算符隐式转换时,GCC与Clang在不同C版本下会选择不同的重载构造函数:仅在Clang搭配C17及更高版本时,拷贝构造函数会被优先于移动构造函数选择。

测试代码

#include <cstdio>
#include <utility>
#include <type_traits>

template <typename T>
class foo {
  T& t_;
public:
  foo(T& t) : t_(t) {}
  operator T() const & { return t_; }
  operator T&&() && { return std::move(t_); }
};

class bar {
  int val_;
public:
  bar(int v) : val_(v) {}
  bar(const bar& b) : val_(b.val_) { printf("copy constructed\n"); }
  bar& operator=(const bar& b)     { printf("copy assigned\n"); val_ = b.val_; return *this; }
  bar(bar&& mv) : val_(mv.val_)    { printf("move constructed\n"); mv.val_ = -1; }
  bar& operator=(bar&& mv)         { printf("move assigned\n"); val_ = mv.val_; mv.val_ = -1; return *this; }
};

int main() {
  bar v(1);
  foo<bar> f(v);
  bar v2(std::move(f));
}

编译测试结果

⟩ clang++-15 -std=c++17 test.cpp && ./a.out
copy constructed

⟩ clang++-15 -std=c++14 test.cpp && ./a.out
move constructed

⟩ g++ -std=c++14 test.cpp && ./a.out
move constructed

⟩ g++ -std=c++17 test.cpp && ./a.out
move constructed

编译器版本信息

⟩ g++ --version
g++ (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

⟩ clang++-15 --version
Ubuntu clang version 15.0.4-++20221102053308+5c68a1cb1231-1~exp1~20221102053355.92
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin

原因分析

这个差异源于C++17对重载决议规则的调整,以及不同编译器的实现细节:

  • C++14及更早版本:编译器优先选择与对象值类别匹配的转换函数。std::move(f)是右值,因此右值限定的operator T&&() &&会被优先选中,其返回的bar&&直接匹配bar(bar&&)移动构造函数,最终输出move constructed。
  • C++17规则变化:C++17调整了隐式转换序列的优先级判定逻辑,当存在多个可行转换路径时,转换到值类型(operator T()返回bar)的序列被视为更直接的匹配。此时Clang严格遵循该规则,选择通过operator T() const &转换得到临时bar对象,再将其绑定到const bar&参数调用拷贝构造函数。
  • 编译器实现差异:GCC在C17版本中仍保留了C14的优先级逻辑,优先选择与右值对象匹配的operator T&&() &&转换函数,因此仍调用移动构造函数。

你的代码并未违反C规则,该现象属于不同编译器对C17重载决议细节的实现差异。

内容的提问来源于stack exchange,提问作者s417-lama

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 04:01:41