concept主要用来定义模板参数的约束,最明显的作用就是在模板参数不满足类型的约束时编译器不再给出几千行奇奇怪怪的错误。当然还有其它的作用,比如说concepts可以用来实现函数的重载、新的concepts可以基于已有的concepts定义从而进行扩展等等
下面以实现通用参数类型转换器来展示concept的使用方法。
假设输入类型 IN, 输出类型 OUT,针对不同的场景,来进行不同的实现
1、IN、OUT一致, 可以直接输出
2、可以通过std::stringstream来进行输入、输出进行转换的
3、复杂容器类型
1、 判断IN、OUT是否一致
cpp
template<typename IN, typename OUT>
concept is_same_type = std::is_same<IN, OUT>::value;
2、判断是否可以进行范围操作
cpp
template <typename BEG, class END>
concept item_range = requires(BEG bg, END ed) {
{ ++bg };
{ *bg };
requires !std::is_void_v<decltype(*bg)>;
{ bg != ed };
};
template <typename T>
concept iter_able = std::is_array_v<T>
|| requires(T value) {
{ value.begin() };
{ value.end() };
requires item_range<decltype(value.begin()), decltype(value.end())>;
}
|| requires(T value) {
{ std::begin(value) };
{ std::end(value) };
requires item_range<decltype(std::begin(value)), decltype(std::end(value))>;
};
3、判断是否可以进程insert操作的容器
cpp
template<typename T>
concept is_container = requires(T res, T::value_type v) {
res.insert(res.begin(), v);
};
4、判断是否符合pair的容器
cpp
template<typename T>
concept is_pair_container = requires(T res, T::value_type v) {
res.emplace(v);
v.first;
v.second;
};
转换器代码
cpp
#include <sstream>
#include <string>
#include <iostream>
#include <map>
#include <vector>
#include <list>
#include <concepts>
//参数转换类
template<typename OUT>
class Convert
{
public:
//输入、输出类型一致
template<typename IN>
requires is_same_type<IN, OUT>
Convert(const IN& val):m_value(val)
{
}
template<typename IN>
requires (!is_same_type<IN, OUT> && !(iter_able<IN> && iter_able<OUT>))
Convert(const IN& val)
{
std::stringstream ss;
ss << val;//向流中传值
ss >> m_value;//向result中写入值
}
Convert(const char* val)
{
std::stringstream ss;
ss << val;//向流中传值
ss >> m_value;//向result中写入值
}
//容器类转换辅助函数
template<typename IN>
requires (is_container<OUT> && !is_pair_container<OUT>)
void ConvertHelper(const IN& val)
{
m_value.insert(m_value.end(), Convert<typename OUT::value_type>(val)());
}
template<typename IN>
requires (!is_container<OUT>)
void ConvertHelper(const IN& val)
{
throw "无效容器";
}
template<typename IK, typename IV>
requires is_pair_container<OUT>
void ConvertHelper(const std::pair<IK, IV>& val)
{
m_value.emplace(Convert<typename std::decay<typename OUT::value_type::first_type>::type>(val.first)(), Convert<typename std::decay<typename OUT::value_type::second_type>::type>(val.second)());
}
template<typename IK, typename IV>
requires (!is_pair_container<OUT>)
void ConvertHelper(const std::pair<IK, IV>& val)
{
throw "pair 类型只支持同类型容器转换";
}
//容器转换
template<typename IN>
requires (!is_same_type<IN, OUT> && iter_able<IN> && iter_able<OUT> )
Convert(const IN& val)
{
for (auto & v : val)
{
ConvertHelper(v);
}
}
OUT operator()()
{
return m_value;
}
public:
OUT m_value;
};
//特化
template<>
class Convert<const char*>
{
public:
//输入、输出类型一致
template<typename IN>
Convert(const IN& val)
{
std::stringstream ss;
ss << val;//向流中传值
m_value = ss.str();
}
const char* operator()()
{
return m_value.c_str();
}
public:
std::string m_value;
};
转换测试
cpp
int main(int argc, char* argv[])
{
//常用类型转换
std::cout << Convert<int>(234)() << std::endl;
std::cout << Convert<int>(std::string("234"))() << std::endl;
std::cout << Convert<std::string>("456")() << std::endl;
std::cout << Convert<double>("456.12")() << std::endl;
std::cout << Convert<const char*>("456.12")() << std::endl;
//容器类型转换
std::vector<std::string> val = {"123", "345", "345"};
auto cValue = Convert<std::vector<int>>(val)();
for(auto item : cValue)
{
std::cout << item << std::endl;
}
//数组向容器转换
int a[] = {1,2,3,4,5};
auto ca = Convert<std::vector<int>>(a)();
for(auto item : ca)
{
std::cout << item << std::endl;
}
//支持pair的容器转换
std::map<std::string, std::string> mval = {{"123", "1"}, {"234", "3"}};
auto cmval = Convert<std::map<int, int>>(mval)();
for(auto item : cmval)
{
std::cout << item.first << ":" << item.second << std::endl;
}
return 0;
}
总结: 实际应用中,各种类型的转换可能复杂的多,但通过实现不难发现,通过concept来定义模板参数的约束, 可以大大简化模板参数类型的萃取工作。