文章目录
- 需求
- 使用
- ref示例
- 构造
- std::make_any
- emplace
- 访问
- std::any_cast<>
- 普通对象转换
- 指针转换
- 引用转换
- 逻辑判断
- ⭐手动实现
- END
需求
在C++这种静态强类型语言中,在一些开发场景下有时需要一种万能类型。
这之后std::any
就应运而生了,any
描述用于任何类型的单个值的类型安全容器。
使用
std::any - cppreference.com
注意std::any
不是一个模板类,但内部实现基本都是模板
ref示例
其实这个示例已经将大多数功能都有所演示了。
#include <any>
#include <iostream>
int main() {
std::cout << std::boolalpha;
// any 类型
std::any a = 1;
std::cout << a.type().name() << ": " << std::any_cast<int>(a) << '\n';
a = 3.14;
std::cout << a.type().name() << ": " << std::any_cast<double>(a) << '\n';
a = true;
std::cout << a.type().name() << ": " << std::any_cast<bool>(a) << '\n';
// 有误的转型
try {
a = 1;
std::cout << std::any_cast<float>(a) << '\n';
} catch (const std::bad_any_cast& e) {
std::cout << e.what() << '\n';
}
// 拥有值
a = 1;
if (a.has_value()) {
std::cout << a.type().name() << '\n';
}
// 重置
a.reset();
if (!a.has_value()) {
std::cout << "no value\n";
}
// 指向所含数据的指针
a = 1;
int* i = std::any_cast<int>(&a);
std::cout << *i << "\n";
}
构造
std::make_any
与大多数make工程函数一样
#include <any>
#include <iostream>
#include <string>
int main() {
auto anyy = std::make_any<std::string>(3, 'a');
std::cout << anyy.type().name() << std::endl;
std::cout << std::any_cast<std::string>(anyy) << std::endl;
}
emplace
#include <any>
#include <iostream>
#include <string>
int main() {
std::any anyy = std::string("123");
std::cout << std::any_cast<std::string>(anyy) << std::endl;
anyy.emplace<std::string>(3, 'a');
std::cout << std::any_cast<std::string>(anyy) << std::endl;
}
访问
std::any_cast<>
std::any_cast<>
是很大的一个讲究。因为涉及各种数据类型问题。
普通对象转换
当检测到数据类型错误时,会抛出异常bad any_cast
#include <any>
#include <iostream>
#include <string>
int main() {
auto a = std::any(12);
std::cout << std::any_cast<int>(a) << '\n';
try {
std::cout << std::any_cast<std::string>(a) << '\n';
} catch (const std::bad_any_cast& e) {
// bad any_cast
std::cout << e.what() << '\n';
}
}
指针转换
#include <any>
#include <iostream>
#include <string>
#include <utility>
void show(std::any& a) {
// 指针示例
// 转换失败为 nullptr
// 成功为 内部实际存储对象地址
if (int* i = std::any_cast<int>(&a)) {
std::cout << "ptr = " << i << " val = " << *i << std::endl;
} else if (std::string* s = std::any_cast<std::string>(&a)) {
std::cout << "ptr = " << s << " val = " << *s << std::endl;
} else {
std::cout << "The anyObj is not a int or string\n";
}
}
int main() {
// 简单示例
auto a = std::any(12);
std::cout << "ptr = " << &a << std::endl;
show(a);
a.emplace<std::string>(std::string("Hello World"));
std::cout << "ptr = " << &a << std::endl;
show(a);
}
某次的测试结果是这样的。
可见内是对对象进行了重新的构造。
ptr = 0x61fe98
ptr = 0x61fe9c val = 12
ptr = 0x61fe98
ptr = 0x8f7a20 val = Hello World
引用转换
自行注意各种应用在使用的时候的区别。
注意转为右值引用时候注意所有权的问题。
#include <any>
#include <iostream>
#include <string>
int main() {
auto a = std::any(std::string("hello"));
std::cout << std::any_cast<std::string>(a) << std::endl;
// T&
auto& ref = std::any_cast<std::string&>(a);
ref[0] = 'H';
std::cout << std::any_cast<std::string>(a) << std::endl;
// const T&
const auto& cref = std::any_cast<const std::string&>(a);
// error
// cref[0] = 'W';
// T&&
// 这里加不加 std::move() 效果都一样
auto&& b = std::any_cast<std::string&&>(std::move(a));
std::cout << "ptr = " << &b << " val = " << b << std::endl;
std::cout << "ptr = " << std::any_cast<std::string>(&a)
<< " val = " << *std::any_cast<std::string>(&a) << std::endl;
}
逻辑判断
因为有没有operator bool()
所以不可以直接在if等逻辑判断中使用。
#include <any>
int main() {
// 简单示例
auto a = std::any(12);
std::cout << std::any_cast<int>(a) << '\n';
if (a.has_value()) {
}
// 没有 operator bool()
// if (a) {}
}
⭐手动实现
借助模板技术,我们可以手动实现一个Any。
首先很使用智能指针可以帮我们减少很多对内存问题的负担。
然后主要是能够做到不同数据类型的切换,普通的方法肯定是不行的。
好在C++中有模板类的技术,通过继承的方式,达到类型擦除的效果。
#include <iostream>
#include <memory>
class Any {
private:
template <typename _Ptr>
using SmartPtr = std::unique_ptr<_Ptr>;
private: // 通过继承的方式达到类型擦除
struct Value {
virtual SmartPtr<Value> clone() = 0;
virtual ~Value() {
}
};
template <typename T>
struct ValueHolder : Value {
T data;
ValueHolder(T t) : data(t) {
}
/**
* !!! 注意,这里使用 std::unique_ptr的话
* 不能直接`return new ValueHolder<T>(data)`
* 自己手写的智能指针,没有这个问题
* =======================================
* error: could not convert
* '(operator new(8), (<statement>,
* ((Any::ValueHolder<int>*)<anonymous>)))' from
* 'Any::ValueHolder<int>*'
* to
* 'Any::SmartPtr<Any::Value> {
* aka std::unique_ptr<Any::Value, std::default_delete<Any::Value>
* >
* }'
*/
virtual SmartPtr<Value> clone() override {
// return new ValueHolder<T>(data)
return SmartPtr<Value>{new ValueHolder<T>(data)};
}
};
private:
SmartPtr<Value> m_value;
public:
template <typename T>
Any(T t) : m_value{new ValueHolder<T>(t)} {
}
Any(const Any &rhs) {
this->operator=(rhs);
}
Any &operator=(const Any &rhs) {
m_value = rhs.m_value->clone();
return *this;
}
public:
// 这里是否返回引用,均可(看调用的最终目的)
template <typename T>
T &value() const {
// 查看是否能够进行类型转换
if (auto p = dynamic_cast<ValueHolder<T> *>(m_value.get())) {
return p->data;
}
// 类型转换失败则抛出异常
throw std::logic_error("value() => Type Error");
}
};
/**
* 检测传参的拷贝操作
*/
void show(Any a) {
}
int main() {
Any anyy = 1;
show(anyy);
try {
anyy.value<int>() += 1;
int inter = anyy.value<int>();
std::cout << typeid(inter).name() << " " << inter << std::endl;
anyy = "abc";
// 这里存的是 const char*
auto str = anyy.value<std::string>();
std::cout << typeid(str).name() << " " << str << std::endl;
} catch (const std::exception &e) {
std::cerr << e.what() << '\n';
}
}
参考:
【C++ STL】C++17 实用 std::any 存储任意类型_哔哩哔哩_bilibili
std::any 详细使用方法 - 哔哩哔哩 (bilibili.com)