当前位置: 首页 > news >正文

跟我学c++高级篇——模板元编程之十三处理逻辑

一、元编程处理逻辑

无论在普通编程还是在元编程中,逻辑的处理,都是一个编程开始的必然经过。开发者对普通编程中的逻辑处理一般都非常清楚,不外乎条件谈判和循环处理。而条件判断常见的基本就是if语句(switch如果不考虑效率等情况,其实都类似)。而循环则有几类for,while等。
而开发者都知道,不管是在什么情况下编程,这种基本的逻辑处理,都是不可避免的。所以要想掌握好元编程,就必须掌握这些基本的逻辑处理,也就是在元编程中,如何进行条件处理和循环处理。

二、条件

在元编程中处理条件逻辑,一般有两大类,即传统的方法和C++新标准下的方法,主要有:
传统方法:
1、三目运算符
这个相对比较简单,看代码:

template<int N>
struct Comp
{constexpr static int result = N > 3 ? 3 : N;
};int main()
{std::cerr << Comp<7>::result << std::endl;
}

2、模拟的特化和偏特化

//偏特化
template<class T, int I> // 主模板
struct A
{void f(); // 成员声明
};template<class T, int I>
void A<T, I>::f() {}      // 主模板成员定义// 部分特化
template<class T>
struct A<T, 2>
{void f();void g();void h();
};
//全特化
#include <type_traits>template<typename T> // 主模板
struct is_void : std::false_type {};
template<>           // 对 T = void 的显式特化
struct is_void<void> : std::true_type {};int main()
{static_assert(is_void<char>::value == false,"对于任何非 void 的类型 T,该类均派生自 false_type");static_assert(is_void<void>::value == true,"但当 T 是 void 时,类派生自 true_type");
}

3、SFINAE技术
比较典型的是std::enable_if,std::is_same等都能实现某种条件判断,如下面的例子:

template<class T>
typename std::enable_if<std::is_trivially_default_constructible<T>::value>::typeconstruct(T*)
{std::cout << "默认构造可平凡默认构造的 T\n";
}

c++11标准及以后:
1、std::conditional
一般开发者在非模板编程中用到还是比较少的,但在模板开发中,还是比较常见的,看代码:

//c++11
#include <iostream>
#include <type_traits>
#include <typeinfo>int main()
{using Type1 = std::conditional<true, int, double>::type;using Type2 = std::conditional<false, int, double>::type;using Type3 = std::conditional<sizeof(int) >= sizeof(double), int, double>::type;std::cout << typeid(Type1).name() << '\n';std::cout << typeid(Type2).name() << '\n';std::cout << typeid(Type3).name() << '\n';
}
//c++17
template<bool B>
using MyType = std::conditional_t<B, int, double>;

2、C++17的 if constexpr

template<typename T>
constexpr auto dowith(T t) {if constexpr (std::is_pointer_v<T>) {return *t;} else if constexpr (std::is_array_v<T>) {return sizeof(T);} else {return t;}
}constexpr int d = 30;
static_assert(dowith(2) == 2, "");
static_assert(dowith(&d) == 30, "");
static_assert(dowith("arr") == 5, "");//编译错误

3、Concepts
这个在前面反复分析过,只给一个简单的例子:

template<class T, class U>
concept Derived = std::is_base_of<U, T>::value;

不同的概念来限制不同的实例的生成,则可以实现条件逻辑的处理。

如果有对这些技术不太熟悉的,可翻看前面的相关文章或直接搜索相关资料,特别是SFINAE的资料,还是比较多的。不过对于未接触过模板编程或者经验较少的开发者来说,学习和应用起来还是有一些难度的。

三、循环

说了条件逻辑处理,就可以看元编程的循环逻辑处理了,在元编程中,循环相对来说要麻烦一些。主要可以分为以下几种:
1、递归模拟
这个最常见的就是斐波那切数列的元编程的例子,在前面也提到过,这里再看一下:

template<int N>
constexpr int factorial() {return N * factorial<N-1>();
}template<>
constexpr int factorial<0>() {return 1;
}static_assert(factorial<5>() == 120, "");

其展开的过程如下:

template<int N>
inline constexpr int factorial()
{return N * factorial<N - 1>();
}/* First instantiated from: insights.cpp:11 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
inline constexpr int factorial<5>()
{return 5 * factorial<5 - 1>();
}
#endif/* First instantiated from: insights.cpp:3 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
inline constexpr int factorial<4>()
{return 4 * factorial<4 - 1>();
}
#endif/* First instantiated from: insights.cpp:3 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
inline constexpr int factorial<3>()
{return 3 * factorial<3 - 1>();
}
#endif/* First instantiated from: insights.cpp:3 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
inline constexpr int factorial<2>()
{return 2 * factorial<2 - 1>();
}
#endif/* First instantiated from: insights.cpp:3 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
inline constexpr int factorial<1>()
{return 1 * factorial<1 - 1>();
}
#endiftemplate<>
inline constexpr int factorial<0>()
{return 1;
}/* PASSED: static_assert(factorial<5>() == 120, ""); */

这里就可以认为递归模拟了循环展开。

2、使用折叠表达式
这种方法应用于一些比较特殊的场景,看代码:

#include <iostream>
#include <tuple>template<typename... Args>
void traverse(const std::tuple<Args...>& t) {auto print = [](const auto&... args) {(..., (std::cout << args << " "));};std::apply(print, t);
}int main() {std::tuple<int, double, std::string> t(1, 2.3, "test");traverse(t);  return 0;
}

3、使用一些模板的技巧实现循环,包括标准迭代的版本

//基本
#include <iostream>
#include <type_traits>template<typename... Ts>
struct TypeContainer {};template<typename Candidate, typename Container>
struct is_contained
{constexpr static bool value = false;
};template<typename Candidate>
struct is_contained<Candidate, TypeContainer<>>
{constexpr static bool value = false;
};template<typename Candidate, typename Cur, typename... Ts>
struct is_contained<Candidate, TypeContainer<Cur, Ts...>>
{constexpr static bool value =std::is_same_v<Candidate, Cur> ||is_contained<Candidate, TypeContainer<Ts...>>::value;
};int main()
{std::cout << is_contained<char, TypeContainer<float, int, double>>::value << std::endl;std::cout << is_contained<double, TypeContainer<float, int, double>>::value << std::endl;std::cout << is_contained<int, TypeContainer<>>::value << std::endl;
}
//c++17
template<typename Candidate, typename... Ts>
struct is_contained;template<typename Candidate, typename... Ts>
struct is_contained<Candidate, TypeContainer<Ts...>>
{constexpr static bool value = []() {if constexpr (sizeof...(Ts) == 0) {return false;} else {return (std::is_same_v<Candidate, Ts> || ...);}}();
};
//c++20
template<typename Candidate, typename... Ts>
constexpr bool is_contained_v = (std::is_same_v<Candidate, Ts> || ...);// 使用示例
static_assert(is_contained_v<double, float, int, double>);
static_assert(!is_contained_v<char, float, int, double>);

4、使用const方式使用传统的for展开
看下面的代码:

#include <iostream>
#include <array>template<typename T, size_t N>
constexpr void traverse(const std::array<T, N>& arr) {for (size_t i = 0; i < N; ++i) {std::cout << arr[i] << " ";}
}int main() {constexpr std::array<int, 4> arr{2, 5, 3, 7};traverse(arr);  return 0;
}

5、使用std::index_sequence
这个就不举例子了,前面有不少。

四、总结

在前面的模板和元编程中,其实对这些技术都有过仔细的分析,只不没有专门的组织起来,形成一个系统的说明。本文主要是让开发者从学习编程语言的习惯来学习一下元编程中的相关技术。
在这个AI有着泛滥的趋势的大形势下,要耐心,切不可浮躁!

http://www.xdnf.cn/news/395857.html

相关文章:

  • E+H流量计profibus协议如何转换成profinet协议
  • C语言_函数调用栈的汇编分析
  • 【AI论文】作为评判者的感知代理:评估大型语言模型中的高阶社会认知
  • 二分查找的理解
  • Object类
  • wordpress自学笔记 第三节 独立站产品和类目的三种展示方式
  • RabbitMQ的工作队列模式和路由模式有什么区别?
  • 2. cef 及 cefcapi
  • 全国青少年信息素养大赛 Python编程挑战赛初赛 内部集训模拟试卷七及详细答案解析
  • Qt开发经验 --- 避坑指南(13)
  • 梦熊联盟:202505基础语法-题解
  • 沐言智语开源Muyan-TTS模型,词错率、语音质量评分都处于开源模型的一线水平,推理速度相当快~
  • Go语言运算符详解
  • No module named ‘xxx’报错原因及解决方式
  • DedeCMS-Develop-5.8.1.13-referer命令注入研究分析 CVE-2024-0002
  • css背景相关
  • 【大模型】解决最新的Dify1.3.1版本 无法基于Ollama成功添加模型
  • 进程间关系与守护进程
  • Quantum convolutional nerual network
  • 责任链模式
  • 苍穹外卖(数据统计–Excel报表)
  • C语言常见的文件操作函数总结
  • 互联网大厂Java求职面试:电商商品推荐系统中的AI技术应用
  • 超标量处理器设计4-分支预测
  • TypeScript 装饰器高级用法详解
  • Kubernetes排错(十四):Pod状态异常排查手册
  • 深入理解 TypeScript 中 unknown 类型:安全性与使用指南
  • 深度学习:系统性学习策略(二)
  • OBS studio 减少音频中的杂音(噪音)
  • LLM初识