在前面分析过std::any和enum class的初步应用,但是在实际应用 中,这两个之间在一起应用时还是要有些小细节需要注意。在程序的开发中,一般来说,不建议直接使用魔数来进行各种情况下的区别。毕竟时间长久后,这些魔数会变得不好理解甚至误解。同样,枚举类的出现导致直接使用枚举的不安全行为得到了控制,但也相应的出现了数据转换的问题。
对于std::any也是如此,它既然可以存储各种数据类型,就会有各种不同的情况出现。如果两者在一起使用,会出现一些具体的问题需要说明一下。
1、枚举类的直接转换
枚举类和普通值的互转用得还是比较多的:
enum class K:int { k1 = 1, k2 };
int a = 10;
K k = k(a);
a = static_cast<int>(k);
这个还是比较简单的。
看一个稍微复杂的:
#include
#include
enum e1 {};
enum class e2 {};
enum class e3: unsigned {};
enum class e4: int {};
int main() {
constexpr bool e1_t = std::is_same_v< std::underlying_type_t, int >;
constexpr bool e2_t = std::is_same_v< std::underlying_type_t, int >;
constexpr bool e3_t = std::is_same_v< std::underlying_type_t, int >;
constexpr bool e4_t = std::is_same_v< std::underlying_type_t, int >;
std::cout
<< "underlying type for 'e1' is " << (e1_t ? "int" : "non-int") << '\n'
<< "underlying type for 'e2' is " << (e2_t ? "int" : "non-int") << '\n'
<< "underlying type for 'e3' is " << (e3_t ? "int" : "non-int") << '\n'
<< "underlying type for 'e4' is " << (e4_t ? "int" : "non-int") << '\n'
;
}
可能输出:
underlying type for 'e1' is non-int
underlying type for 'e2' is int
underlying type for 'e3' is non-int
underlying type for 'e4' is int
2、std::any的处理
一般来说把对象转成std::any直接赋值即可,而反之则需要使用std::any_cast():
std::any x = 10;
int v = std::any<int>(x);
3、枚举类通用转化
在上面的代码中对枚举进行了转换,将普通值转成枚举类还好说一些,直接调用构造函数即可,但反过来则比较麻烦,可以有一个比较通用的方式:
// print enum class
enum class K { k1 = 1, k2 };
enum class T { t1, t2 };
template <typename T> auto print(T const v) -> typename std::underlying_type<T>::type {
return static_cast<typename std::underlying_type<T>::type>(v);
}
void printEnumClass() {
auto k = K::k1;
auto t = T::t2;
std::cout << print(k) << std::endl;
std::cout << print(t) << std::endl;
}
这里使用了std::underlying_type,不过不使用其也可以,直接用static_cast,就看各自的习惯了。
4、std::in_place_type的应用
std::underlying_type有几个相类似的函数,可以应用在std::optional和std::variant和std::any中,这里只是分析一下在std::any中的应用。先看一个例子:
#include
#include
void test_inplace() {
//first
std::any a = 6;
a = std::any{std::in_place_type<std::string>, "test"};
std::any ab{std::in_place_type<long long>, 1};
//second
std::any com{std::complex{1.0, 3.0}};
std::any com_d{std::in_place_type<std::complex<double>>, 2.0, 4.6};
//third
auto ret = [](int a, int b) { return true; };
std::any any_ret{std::in_place_type<std::set<int, decltype(ret)>>, {3, 9}, ret};
}
int main()
{
test_inplace();
return 0;
}
简单的应用就不用介绍了,很容易就明白。上面的代码有几个应用 的不同方式,第一个是如果std::any在已经赋值其它类型后想再给予另外一种类型,就必须使用std::in_place_type来处理,比如上面代码的第一种;而第二种是对处理多个对象参数初始化的情况,也需要使用其来实现;第三种是可以使用std::in_place_type来实现初始化列表,在其后面跟上参数即可。
如果不想使用std::in_place_type,其它它还有一个更方便的函数make_any<>(),这个看来成为了标准库里处理的一个通行方法,一律make_xx一下,问题就解决了。另外,使用std::in_place_type,仍然会出现类似指针退化(数组转化成指针)的情况,这个需要注意。
5、两者共同使用
下面只给一个简单的例子就明白了。
int preStatus = static_cast<int>(std::any_cast<K>(k));
有些细节在应用开发时,可能还会费一些精神。其实重点不是这些细节多难,而是有时候儿遇到之时才会认真去追究这些细节的实现。实践出真知,诚不我欺。