当前位置: 首页 > article >正文

C++中decltype遇到引用类型时的隐藏陷阱

问题描述

使用 decltype 可以获取变量的声明类型。然而,当变量是一个引用类型时,decltype 返回的仍旧是引用类型。这一特性在某些情况下可能会导致意料之外的行为。例如,在尝试使用 decltype 简化代码时,我遇到了以下警告:

'const' qualifier on reference type 'decltype(arg)' (aka 'vector<unordered_map<int, int>> &') has no effect

以下是导致警告的代码片段:

void func(vector<unordered_map<int, int>> &mps) {
    auto aux = [](const decltype(mps)& mps) {
        for (auto &&mp : mps) {
            // Do something
        }
    };
    aux(mps);
}

int main() {
    vector mps{unordered_map<int, int>{}};
    func(mps);
    return 0;
}

在这段代码中,我的目标是让 aux 函数接收一个 const 左值引用,因为我知道在 aux 函数中不会修改这个引用。但是,编译器却发出警告,提示 const 修饰符没有作用。


问题分析

为了进一步理解这个问题,我借助了《Effective Modern C++》中条款 4 提到的一个技巧:通过声明一个未定义的模板类,并在模板实例化时查看编译器的类型推导报错信息。

以下是调整后的代码,加入了类型查看机制:

template <typename T>
class TD;

void func(vector<unordered_map<int, int>> &arg) {
    auto aux = [](const decltype(arg)& mps) {
        TD<decltype(arg)> arg_type;  // 查看推导类型
        TD<decltype(mps)> mps_type;  // 查看推导类型
    };
    aux(arg);
}

编译器报错信息如下:

error: aggregate 'TD<std::vector<std::unordered_map<int, int> >&> arg_type' has incomplete type and cannot be defined
error: aggregate 'TD<std::vector<std::unordered_map<int, int> >&> mps_type' has incomplete type and cannot be defined

从报错信息中可以看出,mps 的推导类型是 std::vector<std::unordered_map<int, int>>&,并没有因为加上 const 而变成 const std::vector<std::unordered_map<int, int>>&

结合编译器的警告信息,我们可以得出以下结论:

  • decltype 返回一个引用类型时,const 修饰符作用在引用本身,而引用本身已经是不可更改的,因此 const 的修饰没有效果。
  • 换句话说,const decltype(arg)& 实际等价于 decltype(arg)&,因为引用的「不可重新绑定」特性使得 const 修饰变得多余。

解决方案

为了实现预期效果(即 aux 函数接收一个 const 左值引用),我们需要去掉 decltype 返回值中的引用部分,再手动加上 const。这可以通过 std::remove_reference_t 实现:

#include <type_traits>

template <typename T>
class TD;

void func(vector<unordered_map<int, int>> &arg) {
    auto aux = [](const std::remove_reference_t<decltype(arg)>& mps) {
        TD<decltype(arg)> arg_type;  // 查看推导类型
        TD<decltype(mps)> mps_type;  // 查看推导类型
    };
    aux(arg);
}

现在,编译器的报错信息如下:

error: aggregate 'TD<std::vector<std::unordered_map<int, int> >&> arg_type' has incomplete type and cannot be defined
error: aggregate 'TD<const std::vector<std::unordered_map<int, int> >&> mps_type' has incomplete type and cannot be defined

可以看到,此时 mps 的类型已经正确地变成了 const std::vector<std::unordered_map<int, int>>&。这说明 std::remove_reference_t 成功地移除了引用部分,使我们可以正确地加上 const


其他方案

虽然使用 std::remove_reference_t 可以解决问题,但这让代码的复杂度增加了。如果上下文较短、类型简单,使用 decltype 反而会让代码变得不直观。在这种情况下,我们建议直接显式写出目标类型,或者通过类型别名进行简化:

方法 1:显式写出类型

直接写出目标类型 const std::vector<std::unordered_map<int, int>>&

void func(vector<unordered_map<int, int>> &mps) {
    auto aux = [](const vector<unordered_map<int, int>>& mps) {
        // Do something
    };
    aux(mps);
}

这种方式更容易阅读,也避免了使用复杂的类型萃取。


方法 2:使用类型别名

如果类型较长,可以通过 using 语句定义一个类型别名:

using MapVector = vector<unordered_map<int, int>>;

void func(MapVector &mps) {
    auto aux = [](const MapVector& mps) {
        // Do something
    };
    aux(mps);
}

这种方式可以减少重复声明,提升代码可读性。


总结

  • 问题根源:decltype 返回一个引用类型时,const 修饰符被认为作用在引用本身,而引用本身已经是不可重新绑定的,因此 const 修饰没有效果。
  • 解决方法: 使用 std::remove_reference_t 去掉引用类型的引用部分,再手动加上 const
  • 优化建议: 在非模板环境中,直接显式写出目标类型或使用类型别名可能更直观。

http://www.kler.cn/a/470821.html

相关文章:

  • 低空管控技术-无人机云监视技术详解!
  • excel如何将小数转换为百分比
  • 51单片机——共阴数码管实验
  • Functions
  • 1/7 C++
  • GWAS数据和软件下载
  • 算法解析-经典150(矩阵、哈希表)
  • Redis - 6 ( 9000 字 Redis 入门级教程 )
  • 【快速入门 LVGL】-- 1、STM32 工程移植 LVGL
  • 深入了解SCPI协议:半导体测试与仪器自动化的核心
  • C语言——字符函数和内存函数
  • pikachu靶场--目录遍历和敏感信息泄露
  • 概率基本概念 --- 离散型随机变量实例
  • NLP 中文拼写检测纠正论文-08-Combining ResNet and Transformer
  • React Router 向路由组件传state参数浏览器回退历史页面显示效果问题
  • Vue.js组件开发-在setup函数中使用provide
  • 41.5 nginx拦截prometheus查询请求使用lua脚本做promql的检查替换
  • 少儿编程|基于SSM+vue的少儿编程管理系统的设计与实现(源码+数据库+文档)
  • 云计算中的可用性SLA
  • Flutter 如何编写 Dart CLI应用程序
  • react 封装一个类函数使用方法
  • 【微服务】4、服务保护
  • 在调用 borrowObject 方法时,Apache Commons Pool 会根据连接池的配置触发一系列相关的方法
  • vue.js 插槽-默认插槽
  • 重温设计模式--13、策略模式
  • MYSQL--------SQL 注入简介MySQL SQL Mode 简介