跟我学C++中级篇—std::conjunction手动实现

一、说明

在前面学习和分析了元编程的逻辑操作。如果在C++17以前没有提供这几个逻辑模板操作应该怎么做呢?虽然已经有了轮子,但是不是可以逆向一下这个轮子,自己尝试着再造一个类似的轮子,会不会能够更好的理解其内在的实现原理和机制呢?

二、定义和实现

在这里仅以std::conjunction为例来进行逻辑模板的实现,先看一下在cppreference上的实现:

c 复制代码
template<class...>
struct conjunction : std::true_type {};
 
template<class B1>
struct conjunction<B1> : B1 {};
 
template<class B1, class... Bn>
struct conjunction<B1, Bn...>
    : std::conditional_t<bool(B1::value), conjunction<Bn...>, B1> {};

再看一下库中的实现:

c 复制代码
template<typename...>
    struct __and_;

  template<>
    struct __and_<>
    : public true_type
    { };

  template<typename _B1>
    struct __and_<_B1>
    : public _B1
    { };

  template<typename _B1, typename _B2>
    struct __and_<_B1, _B2>
    : public __conditional_t<_B1::value, _B2, _B1>
    { };

  template<typename _B1, typename _B2, typename _B3, typename... _Bn>
    struct __and_<_B1, _B2, _B3, _Bn...>
    : public __conditional_t<_B1::value, __and_<_B2, _B3, _Bn...>, _B1>
    { };

  template<typename... _Bn>
    struct conjunction
    : __and_<_Bn...>
    { };

两者的实现基本类似,都是先实现特化版本用来终止变参模板的条件(不明白可以看一下前面变参模板相关的文章),然后conditional_t来递归的处理逻辑类型值的结果。

三、实现源码及分析

下面看看用别的方法是否也可以实现类似的代码逻辑操作。看下面的代码:

c 复制代码
#include <iostream>
#include <type_traits>

// 普通模板,空参数返回true_type
template <typename...> struct __and__ : std::true_type {};

// 递归特化并检查条件
template <typename T, typename... Rest> struct __and__<T, Rest...> : std::integral_constant<bool, T::value && __and__<Rest...>::value> {};

// 模拟AND实现
template <typename... Cond> using __and_t__ = __and__<Cond...>;

// 多and检测
template <typename T, typename = void> struct check_mul_attr : std::false_type {};

template <typename T>
struct check_mul_attr<T, std::void_t<decltype(std::declval<T>().test()), decltype(std::declval<T>().display()), typename T::nestType>>
    : __and_t__<std::is_same<decltype(std::declval<T>().test()), void>, std::is_same<decltype(std::declval<T>().display()), int>,
                std::is_convertible<typename T::nestType, int>> {};

// 测试结构体
struct AllAttr {
//static void test(){}
  void test() {}
  int display() { return 0; }
  using nestType = int;
};

struct PartialAttr {
  void test() {}
  using nestType = int;
};

struct Nothing {};

int main() {
  // 测试AND逻辑
  std::cout << "all have: " << check_mul_attr<AllAttr>::value << std::endl;    // 1
  std::cout << "partial: " << check_mul_attr<PartialAttr>::value << std::endl; // 0
  std::cout << "nothing: " << check_mul_attr<Nothing>::value << std::endl;     // 0

  // 测试__and_t__
  std::cout << "test and result True: " << __and_t__<std::true_type, std::true_type>::value << std::endl;   // 1
  std::cout << "test and result False: " << __and_t__<std::true_type, std::false_type>::value << std::endl; // 0

  return 0;
}

编译展开后的代码为:

c 复制代码
#include <iostream>
#include <type_traits>

template<typename ... type_parameter_0_0>
struct __and__ : public std::integral_constant<bool, true>
{
};

/* First instantiated from: insights.cpp:8 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct __and__<std::is_same<int, int>, std::is_convertible<int, int> > : public std::integral_constant<bool, true>
{
};

#endif
/* First instantiated from: insights.cpp:8 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct __and__<std::is_convertible<int, int> > : public std::integral_constant<bool, true>
{
};

#endif
/* First instantiated from: insights.cpp:8 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct __and__<> : public std::integral_constant<bool, true>
{
};

#endif
/* First instantiated from: insights.cpp:8 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct __and__<std::integral_constant<bool, true> > : public std::integral_constant<bool, true>
{
};

#endif
/* First instantiated from: insights.cpp:8 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct __and__<std::integral_constant<bool, false> > : public std::integral_constant<bool, false>
{
};

#endif
/* First instantiated from: insights.cpp:18 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct __and__<std::is_same<void, void>, std::is_same<int, int>, std::is_convertible<int, int> > : public std::integral_constant<bool, true>
{
};

#endif
/* First instantiated from: insights.cpp:43 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct __and__<std::integral_constant<bool, true>, std::integral_constant<bool, true> > : public std::integral_constant<bool, true>
{
};

#endif
/* First instantiated from: insights.cpp:44 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct __and__<std::integral_constant<bool, true>, std::integral_constant<bool, false> > : public std::integral_constant<bool, false>
{
};

#endif

template<typename T, typename ... Rest>
struct __and__<T, Rest...> : public std::integral_constant<bool, T::value && __and__<Rest...>::value>
{
};


template<typename ... Cond>
using __and_t__ = __and__<Cond...>;

template<typename T, typename type_parameter_0_1 = void>
struct check_mul_attr : public std::integral_constant<bool, false>
{
};

/* First instantiated from: insights.cpp:38 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct check_mul_attr<AllAttr, void> : public __and__<std::is_same<void, void>, std::is_same<int, int>, std::is_convertible<int, int> >
{
};

#endif
/* First instantiated from: insights.cpp:39 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct check_mul_attr<PartialAttr, void> : public std::integral_constant<bool, false>
{
};

#endif
/* First instantiated from: insights.cpp:40 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct check_mul_attr<Nothing, void> : public std::integral_constant<bool, false>
{
};

#endif

template<typename T>
struct check_mul_attr<T, std::void_t<decltype(std::declval<T>().test()), decltype(std::declval<T>().display()), typename T::nestType> > : public __and_t__<std::is_same<decltype(std::declval<T>().test()), void>, std::is_same<decltype(std::declval<T>().display()), int>, std::is_convertible<typename T::nestType, int> >
{
};


struct AllAttr
{
  inline void test()
  {
  }
  
  inline int display()
  {
    return 0;
  }
  
  using nestType = int;
};


struct PartialAttr
{
  inline void test()
  {
  }
  
  using nestType = int;
};


struct Nothing
{
};


int main()
{
  std::operator<<(std::cout, "all have: ").operator<<(std::integral_constant<bool, true>::value).operator<<(std::endl);
  std::operator<<(std::cout, "partial: ").operator<<(std::integral_constant<bool, false>::value).operator<<(std::endl);
  std::operator<<(std::cout, "nothing: ").operator<<(std::integral_constant<bool, false>::value).operator<<(std::endl);
  std::operator<<(std::cout, "test and result True: ").operator<<(std::integral_constant<bool, true>::value).operator<<(std::endl);
  std::operator<<(std::cout, "test and result False: ").operator<<(std::integral_constant<bool, false>::value).operator<<(std::endl);
  return 0;
}

这段代码中需要说明的std::void_t对于非正常类型是无法转换出void的,所以正常情况下就检查到了包含内部的函数或属性等。另外,还需要注意decltype(std::declval().test())这段代码,如果在T的属性中,test函数声明为静态的,则可以直接使用decltype(T::test()),否则就只能按现在的情况先使用declval在编译期生成一个对象然后再调用test函数的类型检测。

check_mul_attr多and检测默认是false_type,然后偏特化另外一个检测版本,当上面代码说明中的void成功转换后则选中这个特化版本,即可进行连续的and控制。

四、总结

重复造轮子好不好没有一个标准的答案,但如果造轮子是出于学习的目的,如果这个轮子规模不太大的情况下,一定是个好事。学习最忌的是"知其然不知其所以然",因为这种情况下,往往无法灵活主动的有机结合各种技术解决问题。大多数情况下往往是高级的"生搬硬套",这就会带来各种各样的问题。关键是这些问题不一定会在当下暴露出来,然而当其真正暴露出来时,就可能是一个大问题。

相关推荐
项目題供诗2 小时前
C语言基础(三)
c语言·c++
1***43802 小时前
C++跨平台开发的核心挑战线程管理等基础功能
开发语言·c++
txinyu的博客3 小时前
C++ 智能指针 (shared_ptr/weak_ptr) 全解析
开发语言·c++
小徐不徐说3 小时前
避坑指南:Qt 中 Lambda 表达式崩溃原因与高效使用实践
数据库·c++·qt·面试
寻星探路3 小时前
【算法进阶】滑动窗口与前缀和:从“和为 K”到“最小覆盖子串”的极限挑战
java·开发语言·c++·人工智能·python·算法·ai
txinyu的博客3 小时前
C++ 模板元编程 (TMP)
开发语言·c++
dragoooon343 小时前
C++ 从零实现Json-Rpc 框架
开发语言·c++·rpc
三万棵雪松4 小时前
【AI小智硬件程序(八)】
c++·人工智能·嵌入式·esp32·ai小智
王老师青少年编程4 小时前
2025年12月GESP真题及题解(C++七级): 学习小组
c++·gesp·csp·信奥赛·七级·csp-s·提高组