C++ 20 Concept

复制代码
    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来定义模板参数的约束, 可以大大简化模板参数类型的萃取工作。

相关推荐
小小龙学IT4 天前
C++20 Ranges 库实现原理深度解析:从视图适配器到管道运算符
c++20
Billy121388 天前
C++20 Concepts:约束与概念的现代范式
c++20·c++进阶学习
可爱系程序猿8 天前
mfc90.dll 缺失如何处理?VC++ 2008 运行库与并行程序集排查记录
程序人生·电脑·c++20
code_pgf8 天前
C++11 / C++14 / C++17 / C++20 新特性总结
c++·c++20
C语言Plus10 天前
C++ SqlBuilder一个简单、灵活且类型安全的 C++ SQL 构建器库
数据库·sql·安全·c++20
CHANG_THE_WORLD20 天前
C++20 协程从零入门:用 ASCII 图彻底理解 `co_await`、协程帧与异步执行
c++20
无限的鲜花1 个月前
协程本质是函数加状态机——零基础深入浅出 C++20 协程
c++·算法·c++20
十五年专注C++开发2 个月前
全面深入了解C++20 范围库(std::ranges)
c++20·管道·哨兵·视图·ranges
小小龙学IT2 个月前
C++20 协程深度解析:从原理到高性能异步框架实战
junit·c++20
楼田莉子2 个月前
C++20新特性:协程
开发语言·c++·后端·学习·c++20