c++ - 如何专门化容器和枚举的模板
问题描述
我正在尝试为枚举类型和 stl 容器类型专门提供一个简单的功能。SFINAE 的想法适用于使用 enable_if 的枚举,但是类似的 stl 容器技术不起作用(我知道依赖 value_type 的存在并假设它是容器不是一个好主意,但这不是重点)。
template <typename T, typename = void>
struct wrapper {
static T getValue()
{
std::cout<<"\n WRAPPER DEFAULT VERSION IS CALLED.\n";
return T();
}
};
template<typename T>
struct wrapper<T,typename std::enable_if<std::is_enum<T>::value>::type>{
static T getValue()
{
std::cout<<"\n WRAPPER ENUM VERSION IS CALLED.\n";
return T();
}
};
template<typename Container>
struct wrapper<Container, typename Container::value_type> {
static Container getValue()
{
std::cout<<"\n WRAPPER CONTAINER VERSION IS CALLED.\n";
return Container();
}
};
int main()
{
//En is an enum type
En en = (En) wrapper<En>::getValue(); //Prints ENUM VERSION
std::vector<int> vec;
vec = wrapper<std::vector<int>>::getValue(); //Prints DEFAULT VERSION
}
请让我知道为什么第二次调用会转到默认实现?
解决方案:
感谢 Sam Varshavchik,我发现我错过了第二个参数应该解析为 void 的点(就像在 enable_if::type 的情况下一样),否则我必须显式传递第二个参数才能使我的调用解析为容器版本:
wrapper<std::vector<int>, int>::getValue();
为了使原始版本正常工作(在 void_t 不可用的 C++ 17 之前),我正在创建自己的类型特征,这依赖于容器为它们定义了迭代器类型的事实:
template <typename T1, typename T2 = void>
struct container_trait
{};
template <typename T1>
struct container_trait<T1, typename T1::iterator> {
typedef void type;
};
现在我的容器版本的包装器变为:
template<typename Container>
struct wrapper<Container, typename container_trait<Container, typename Container::iterator>::type> {
static Container getValue(const rapidjson::Value& rjv)
{
std::cout<<"\n WRAPPER CONTAINER VERSION IS CALLED.\n";
return Container();
}
};
现在同样的调用工作得很好:
vec = wrapper<std::vector<int>>::getValue(); //Prints CONTAINER VERSION
解决方案
第二次调用默认实现的原因很简单。
只需手动计算如何为容器版本模板的参数推导参数:
template<typename Container>
struct wrapper<Container, typename Container::value_type>
您正在实例化以下模板:
wrapper<std::vector<int>>
所以:
1)Container
是一个std::vector<int>
2)Container::value_type
是int
因此,该专业化变为:
struct wrapper<std::vector<int>, int>
但是,您只调用:
wrapper<std::vector<int>, void>
因为void
是第二个模板参数的默认值,所以这匹配了错误的特化。
解决方案很简单,容器专业化应该是:
#include <type_traits>
template<typename Container>
struct wrapper<Container, std::void_t<typename Container::value_type>> {
std::void_t
是 C++17,stackoverflow.com 上还有其他问题解释了如何为早期的 C++ 标准实现它。完整示例:
#include <vector>
#include <iostream>
#include <type_traits>
enum En {};
template <typename T, typename = void>
struct wrapper {
static T getValue()
{
std::cout<<"\n WRAPPER DEFAULT VERSION IS CALLED.\n";
return T();
}
};
template<typename T>
struct wrapper<T,typename std::enable_if<std::is_enum<T>::value>::type>{
static T getValue()
{
std::cout<<"\n WRAPPER ENUM VERSION IS CALLED.\n";
return T();
}
};
template<typename Container>
struct wrapper<Container, std::void_t<typename Container::value_type>> {
static Container getValue()
{
std::cout<<"\n WRAPPER CONTAINER VERSION IS CALLED.\n";
return Container();
}
};
int main()
{
//En is an enum type
En en = (En) wrapper<En>::getValue(); //Prints ENUM VERSION
std::vector<int> vec;
vec = wrapper<std::vector<int>>::getValue(); //Prints DEFAULT VERSION
}
结果:
调用了包装器枚举版本。
包装容器版本被调用。
推荐阅读
- libgdx - ClickListener 仅适用于 Table Libgdx 中的元素
- python - Python 3.6:在 AWS Ubuntu 上写入文件时忽略新行命令“/n”
- sql - 按子句顺序使用列名时出现歧义错误
- jquery - 动画 - Css 中的滚动顶部
- javascript - 在多个 Vue 应用程序之间共享路由的最佳实践是什么?
- python - 比较裁剪的图像 python opencv
- laravel - 如何在单元测试 Laravel 中获取电子邮件验证链接
- php - 从链接 PHP 生成 PDF
- java - 在类型安全配置中访问完全解析的配置
- r - 散点图中每个因素的统计摘要 ggplot2:fun.x、fun_y 组合怎么样?