吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 585|回复: 7
收起左侧

[学习记录] 用C++代码判断左值、纯右值、将亡值

  [复制链接]
a2025 发表于 2025-12-21 18:28
本帖最后由 a2025 于 2025-12-21 18:43 编辑

前段时间在研究C++的移动语义、万能引用、完美转发,不可避免地涉及到了左值、纯右值、将亡值。

简单来说,万能引用会保留入参的值得类别(左值、右值),
如果入参是左值,那么形参是左值引用,
如果入参是右值,那么形参是右值引用,
所以,可以使用万能引用判断一个变量是左值还是右值,如下:
[C++] 纯文本查看 复制代码
#include <iostream>
#include <type_traits>
template <typename T>
char get_LR_value(T&& param) { // T&& 在这里是万能引用(转发引用)
  if (std::is_lvalue_reference_v<decltype(param)>) {
    return 'L'; // 左值
  } else if (std::is_rvalue_reference_v<decltype(param)>) {
    return 'R'; // 右值
  } else {
    return '-'; // 按值传递(但这种情况在万能引用中不会发生)
  }
}
int main() {
  int a = 3;
  std::cout << "LR: " << get_LR_value(a) << ", a" << std::endl;
  std::cout << "LR: " << get_LR_value(std::move(a)) << ", std::move(a)" << std::endl;
  std::cout << "LR: " << get_LR_value(3) << ", 3" << std::endl;
}


因为(右值=纯右值+将亡值),我怎么区分出来纯右值+将亡值呢?
后来我找到了 https://zh.cppreference.com/w/cpp/language/decltype.html
它的大概意思是:
[Plain Text] 纯文本查看 复制代码
decltype((expr)) 的魔法:
    decltype(expr) 给出表达式的类型
    decltype((expr)) 给出表达式的值类别信息
    额外的括号让 decltype 分析表达式而不是实体
C++标准保证的行为:
    如果 (expr) 是左值,,decltype((expr)) 产生 T&
    如果 (expr) 是将亡值,decltype((expr)) 产生 T&&
    如果 (expr) 是纯右值,decltype((expr)) 产生 T

那么,我就可以根据形参是左值引用、右值引用、非引用类型,判断入参是左值、将亡值、纯右值了,如下:
[C++] 纯文本查看 复制代码
#include <iostream>
#include <type_traits>
template <typename T2>
static const char* value_category() {
  if (std::is_lvalue_reference_v<T2>) {
    return "Lvalue";
  } else if (std::is_rvalue_reference_v<T2>) {
    return "Xvalue";
  } else {
    return "pRvalue";
  }
}
int main() {
  int a = 3;
  std::cout << "LR: " << value_category<decltype((a))>() << ", a" << std::endl;
  std::cout << "LR: " << value_category<decltype((std::move(a)))>() << ", std::move(a)" << std::endl;
  std::cout << "LR: " << value_category<decltype((3))>() << ", 3" << std::endl;
}

至此,用C++代码判断一个变量是左值、纯右值、将亡值的核心代码逻辑已经说完了。

后来,因为decltype(())写着太麻烦,加之,我又想获取变量的一些额外信息,结果代码越写越多,最后变成了下面的样子。
可以直接拉到最下面,先看看用法,然后运行一下。感觉有用的再行取用:
(Windows无法直接运行,可以尝试注释掉<cxxabi.h>,并修改demangle函数,使其直接返回入参,再运行)
[C++] 纯文本查看 复制代码
#include <cxxabi.h> // abi::__cxa_demangle
#include <iostream>
#include <type_traits>
std::string demangle(const char* mangled_name) {
  int status = -1;
  char* demangled_name = abi::__cxa_demangle(mangled_name, nullptr, nullptr, &status);
  std::string result;
  if (status == 0) {
    result = demangled_name;
    std::free(demangled_name); // 释放函数分配的内存
  } else {
    result = mangled_name;
  }
  return result;
}
#define VALUE_CATEGORY(var) TypeInspector::value_category<decltype((var))>()
#define PRINT_TYPE_DESC(var) std::cout << #var << " | " << TypeInspector::calc_type_desc<decltype(var), decltype((var))>() << std::endl
#define PRINT_TYPE_INFO(var, show_all) TypeInspector::print_full_info<decltype(var), decltype((var))>(#var, show_all)
#define STRIP_PARENS(x) STRIP_PARENS_IMPL x
#define STRIP_PARENS_IMPL(...) __VA_ARGS__
#define VALUE_CATEGORY2(var) TypeInspector::value_category<decltype((STRIP_PARENS(var)))>()
#define PRINT_TYPE_DESC2(var) std::cout << #var << " | " << TypeInspector::calc_type_desc<decltype(STRIP_PARENS(var)), decltype((STRIP_PARENS(var)))>() << std::endl
#define PRINT_TYPE_INFO2(var, show_all) TypeInspector::print_full_info<decltype(STRIP_PARENS(var)), decltype((STRIP_PARENS(var)))>(#var, show_all)
class TypeInspector {
public:
  template <typename T2>
  static const char* value_category() {
    if (std::is_lvalue_reference_v<T2>) {
      return "Lvalue";
    } else if (std::is_rvalue_reference_v<T2>) {
      return "Xvalue";
    } else {
      return "pRvalue";
    }
  }
  template <typename T1, typename T2>
  static std::string calc_type_desc() {
    std::string s;
    std::is_const_v<std::remove_reference_t<T1>> ? s += "const " : s;
    std::is_volatile_v<std::remove_reference_t<T1>> ? s += "volatile " : s;
    s += demangle(typeid(std::remove_pointer_t<std::remove_cv_t<std::remove_reference_t<T1>>>).name());
    std::is_pointer_v<T1> ? s += " *" : s;
    std::is_lvalue_reference_v<T1> ? s += " &" : s;
    std::is_rvalue_reference_v<T1> ? s += " &&" : s;
    s = s + "    (" + value_category<T2>() + ")";
    return s;
  }
  template <typename T1, typename T2>
  static void print_full_info(const std::string& name = "", bool show_all = true) {
    using BaseType = std::remove_cv_t<std::remove_reference_t<T1>>;
    std::cout << "=== " << (name.empty() ? "unknown" : name) << " ===" << std::endl;
    std::cout << "|typeid(T1)|Raw type name: " << demangle(typeid(T1).name()) << std::endl;
    std::cout << "Calculate_Type_Descriptor: " << calc_type_desc<T1, T2>() << std::endl;
    std::cout << "Calculated_Value_Category: " << value_category<T2>() << std::endl;
    // CV限定符
    print_info("is_const", std::is_const_v<std::remove_reference_t<T1>>, show_all);
    print_info("is_volatile", std::is_volatile_v<std::remove_reference_t<T1>>, show_all);
    // 引用和指针
    print_info("is_reference", std::is_reference_v<T1>, show_all);
    print_info("is_lvalue_reference", std::is_lvalue_reference_v<T1>, show_all);
    print_info("is_rvalue_reference", std::is_rvalue_reference_v<T1>, show_all);
    print_info("is_pointer", std::is_pointer_v<T1>, show_all);
    // 类型分类
    print_info("is_fundamental", std::is_fundamental_v<BaseType>, show_all);
    print_info("is_arithmetic", std::is_arithmetic_v<BaseType>, show_all);
    print_info("is_integral", std::is_integral_v<BaseType>, show_all);
    print_info("is_floating_point", std::is_floating_point_v<BaseType>, show_all);
    print_info("is_array", std::is_array_v<BaseType>, show_all);
    print_info("is_enum", std::is_enum_v<BaseType>, show_all);
    print_info("is_union", std::is_union_v<BaseType>, show_all);
    print_info("is_class", std::is_class_v<BaseType>, show_all);
    print_info("is_function", std::is_function_v<BaseType>, show_all);
    // 类型修饰符
    print_info("is_signed", std::is_signed_v<BaseType>, show_all);
    print_info("is_unsigned", std::is_unsigned_v<BaseType>, show_all);
    // 类型操作属性
    print_info("is_trivially_copyable", std::is_trivially_copyable_v<BaseType>, show_all);
    print_info("is_trivial", std::is_trivial_v<BaseType>, show_all);
    print_info("is_standard_layout", std::is_standard_layout_v<BaseType>, show_all);
    print_info("is_pod", std::is_pod_v<BaseType>, show_all);
    // 内存管理相关
    print_info("is_destructible", std::is_destructible_v<BaseType>, show_all);
    print_info("is_trivially_destructible", std::is_trivially_destructible_v<BaseType>, show_all);
    // 大小信息
    std::cout << "| sizeof(BaseType)|Size: " << sizeof(BaseType) << " bytes" << std::endl;
    std::cout << "|alignof(BaseType)|Alignment: " << alignof(BaseType) << " bytes" << std::endl;
    std::cout << "-------------------" << std::endl;
  }

private:
  static void print_info(const std::string& name, bool value, bool show_all) {
    if (show_all || value) {
      std::cout << (value ? "v" : "x") << " : " << name << std::endl;
    }
  }
};
int main() {
  // 用法如下:
  // 仅查看:左值、纯右值、将亡值
  std::cout << VALUE_CATEGORY("abc") << std::endl;
  std::cout << std::endl;

  // 简短查看类型情况
  PRINT_TYPE_DESC("abc");
  std::cout << std::endl;
  PRINT_TYPE_DESC2(("abc")); // 后缀有2的宏,是复杂的宏替换,这里写在一起,用于对比写法
  std::cout << std::endl;

  // 详细查看类型情况
  PRINT_TYPE_INFO("abc", 0);
  std::cout << std::endl;

  // 简单的宏替换会报错时,可以使用复杂的宏替换,如下:
  PRINT_TYPE_INFO2((std::pair<int, float>(3, 3.14).first), 0);
}

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

guangxin 发表于 2025-12-22 03:00
试着运行了一下代码,能正确地编译输出,谢谢!
kevin10086 发表于 2025-12-22 08:21
a42010316 发表于 2025-12-22 08:29
51cbb 发表于 2025-12-22 08:41
我是来学习的,厉害了!
可坏 发表于 2025-12-22 11:29
c++ 越搞越复杂了 很无语
yvyvsi 发表于 2025-12-22 11:31
试了一下代码,能正确地编译输出
ESMGAL 发表于 2025-12-24 18:57
左值、右值都是老概念,无需赘述。实际上就是增加了一个右值引用的概念,也就是楼主提到的将亡值。通俗来说就是 T&& v; 任意类型变量v,包括常量,都可推导出未知类型T; std::forward<T>(v)保证返回的类型与v的类型相同(也是T&&, 注意T&&展开后未必带两个&),并且如果v的类型为&&,返回临时变量。变量除了有类型属性外(&&也是类型属性),还有是否是临时的属性。 此外std::move(v)会将具名变量变为右值引用的语意。  说白了就是为了提升效率,就是告诉编译器这个变量后面用不到了,你随便优化掉就完事了,仅此而已。具体可以看看std::vector近几次的拷贝变化
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-12-25 14:24

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表