C++17 收编了boost库的 boost::any , boost::optional, boost::variant
any可以模仿动态类型语言的变量。注意,any不是模板类,因此,可以这样做:
#include <iostream> #include <any> #include <vector> int main() { using namespace std; vector<any> myvec={ any(10), any("abc"), any(3.14f) }; for( auto& a : myvec){ if (a.type() == typeid(int)){ int i = std::any_cast<int>(a); std::cout <<"i="<<i<< std::endl; } else if (a.type() == typeid(const char*)) { const char* str = std::any_cast<const char*>(a); std::cout <<"str="<<str<< std::endl; } else if (a.type() == typeid(float)){ float d = std::any_cast<float>(a); std::cout <<"d="<<d<< std::endl; } } }
考虑一个函数返回用户信息。当用户存在时,需要想办法告知调用者。可以这样:
#include <iostream>#include <string> struct User{ int user_id; std::string name; }; User query_user_by_uid(int user_id){ //查询用户资料,返回对象 //... //当用户不存在时,返回空的用户对象 return User{-1,""}; } int main() { using namespace std; User u = query_user_by_uid(12345); if (u.user_id==-1){ std::cout << "用户不存在" << std::endl; } }
其它方式:返回个(User*)0空指针,或者抛例外no_user_found_exception,等等。
如果对上述方式都不满意,可以使用optional, 代码如下:
#include <iostream> #include <optional> #include <string> struct User{ int user_id; std::string name; User(int i, std::string n){ //构造函数 user_id = i; name = n; } }; std::optional<User> query_user_by_uid(int user_id){ //查询用户资料,返回对象 //... return std::optional<User>(std::in_place, user_id, "hello"); //当用户不存在时,返回空的用户对象 return std::optional<User>(); } int main() { using namespace std; std::optional<User> u = query_user_by_uid(12345); if (!u){ std::cout << "用户不存在" << std::endl; } else{ auto& user = *u; std::cout << "user name=" << user.name << std::endl; } }
注意,std::in_place是个全局常量,用于告知optional创建User对象的方式是in place construct。不熟悉placement new的同学可以去搜索相关知识。
最后一个兄弟是variant。能替换union类,是更好的解决方案。代码如下:
#include <iostream> #include <variant> #include <string> struct User{ int user_id; std::string name; User(int i, std::string n){ //构造函数 user_id = i; name = n; } }; int main() { std::variant<std::string, int, User> user( std::in_place_type<User>, 12345, "hello"); //in place构造User对象 std::variant<std::string, int, User> i( 12345 ); std::variant<std::string, int, User> str( "hello" ); std::variant<std::string, int, User> nul_str(); //构造的索引0位置的对象,就是构造一个std::string std::variant<User, int> wft(); //User没有默认构造函数,因此编译报错 std::variant<std::monostate, User, int> ok(); //默认按照索引0对应的类型构造(让User避开索引0),默认构造一个monostate对象(占位用)。 std::cout << "存放的是第"<< user.index() << "个类型的数据" << std::endl; //索引0开始,2代表User user = 123; std::cout << "存放的是第"<< user.index() << "个类型的数据 " << std::get<int>(user) <<std::endl; //索引1开始,1代表int user.emplace<std::string>("world"); //in place construct std::cout << "存放的是第"<< user.index() << "个类型的数据 " << std::get<std::string>(user) <<std::endl; //索引1开始,1代表int }
查询当前存放的数据的类型(通过index()函数),存放,读取,全部满足要求!还有什么不满足的呢?那就是Mordern C++化!!代码如下:
#include <iostream> #include <variant> #include <string> struct User{ int user_id; std::string name; User(int i, std::string n){ //构造函数 user_id = i; name = n; } }; void handleData(int i){ std::cout << "i="<< i; } void handleData(const User &d){ std::cout << "User.name=" << d.name ; } void handleData(const std::string &s){ std::cout << "s=" << s; } int main() { std::variant<std::string, int, User> user( std::in_place_type<User>, 12345, "hello"); //in place构造User对象 std::visit( [](auto const & val) { handleData(val); }, user); }
原理待研究