好友
阅读权限10
听众
最后登录1970-1-1
|
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);
}
|
|