CPP静态“多态”的几种实现方案

2023-12-15 10:40:09

我们这里说的多态,不单指C++中的多态语法,广义的来讲,就是根据传入参数数量或者参数类型的差异,执行不同的动作。现在我们有这样的业务场景:在C++中,根据参数数量改变行为的场景下,有以下几种常见的解决方案:

1. 函数重载

这是最直接的方法,你可以为每个参数数量定义一个不同的函数。但是,这种方法在参数数量较多时会变得很繁琐。

template<typename Ty>
void doSt( Ty v)
{
    std::cout << "[" << __FUNCTION__ << ":"  << __LINE__ << "] " <<  v << std::endl;
}

/// 模板特化
template<>
void doSt(int v)
{
    std::cout << "[" << __FUNCTION__ << ":"  << __LINE__ << "] " <<  v << std::endl;
}


/// 模板函数重载 : 两个参数,参数一致
template<typename Ty>
void doSt(Ty v1, Ty v2)
{
    std::cout << "[" << __FUNCTION__ << ":"  << __LINE__ << "] " << v1 << " " << v2 << std::endl;
}


/// 模板函数重载:两个参数,参数不同
template <typename _Ty1,typename _Ty2,
          typename std::enable_if<!std::is_same<_Ty1, _Ty2>::value>::type* = nullptr >
void doSt(_Ty1 v1,_Ty2 v2)
{
 std::cout << "[" << __FUNCTION__ << ":"  << __LINE__ << "] " << v1 << " " << v2 << std::endl;
}


/// ... 多个参数,多种参数类型
///  我麻了



void foo_()
{
    doSt<int>(10);
    doSt<int>(2,3);
    doSt<int,std::string>(8,"b");
    
    // ...
}

2. 可变参数模板

C++11引入了可变参数模板,它可以接受任意数量的参数。你可以在函数模板中使用可变参数模板,然后在函数体中使用递归或者折叠表达式来处理所有的参数。

// 定义处理函数
template<typename T>
void handle(const T& t) 
{
    std::cout << "General handle for type: " << typeid(T).name() << ", value: " << t << std::endl;
}

// 为特定类型特化处理函数
template<>
void handle<int>(const int& t) 
{
    std::cout << "Handle for int: " << t << std::endl;
}

template<>
void handle<std::string>(const std::string& t)
 {
    std::cout << "Handle for string: " << t << std::endl;
}

// 使用可变参数模板处理任意数量的参数
template<typename T>
void process(const T& t)
 {
    handle(t);
}

 template<typename T, typename... Args>
 void process(const T& t, const Args&... args)
  {
     handle(t);
     process(args...);  // 递归调用,处理剩余的参数
 }

3. std::initializer_list

如果所有的参数都是同一类型,你可以使用 std::initializer_list 来接受任意数量的参数。然后,你可以遍历这个 std::initializer_list 来处理所有的参数。

#include <iostream>
#include <initializer_list>
template <typename _Ty>
void print(std::initializer_list<_Ty> args) 
{
    for (const auto& arg : args) {
        std::cout << arg << " ";
    }
    std::cout << std::endl;
}

void test_print()
{
    print<int>({1, 2, 3, 4, 5});  // 输出:1 2 3 4 5
    print<std::string>({"this","is", "a","joke~"});
}

4. 宏

如你所见,你也可以使用宏来实现这种效果。通过定义一系列的宏,你可以根据参数数量来选择使用哪一个宏。然而,这种方法在C++中并不常见,因为它的可读性和可维护性都不如上述的方法。

#include <iostream>
#include <string>
using namespace std;

#define GLUE(x, y) x y

#define RETURN_ARG_COUNT(_1_, _2_, _3_, _4_, _5_, count, ...) count
#define EXPAND_ARGS(args) RETURN_ARG_COUNT args
#define COUNT_ARGS_MAX5(...) EXPAND_ARGS((__VA_ARGS__, 5, 4, 3, 2, 1, 0))

#define OVERLOAD_MACRO2(name, count) name##count
#define OVERLOAD_MACRO1(name, count) OVERLOAD_MACRO2(name, count)
#define OVERLOAD_MACRO(name, count) OVERLOAD_MACRO1(name, count)

#define CALL_OVERLOAD(name, ...) GLUE(OVERLOAD_MACRO(name, COUNT_ARGS_MAX5(__VA_ARGS__)), (__VA_ARGS__))

#define ERROR1(title) printf("Error: %s\n", title)
#define ERROR2(title, message)\
    ERROR1(title);\
    printf("Message: %s\n", message)
#define ERROR(...) CALL_OVERLOAD(ERROR, __VA_ARGS__)

#define ASSERT1(expr) singleArgumentExpansion(expr)
#define ASSERT2(expr, explain) twoArgumentExpansion(expr, explain)
#define ASSERT(...) CALL_OVERLOAD(ASSERT, __VA_ARGS__)


#define ERROR1(title) printf("Error: %s\n", title)
#define ERROR2(title, message)\
    ERROR1(title);\
    printf("Message: %s\n", message)

#define ERROR_CHOOSE_HELPER2(count) ERROR##count
#define ERROR_CHOOSE_HELPER1(count) ERROR_CHOOSE_HELPER2(count)
#define ERROR_CHOOSE_HELPER(count) ERROR_CHOOSE_HELPER1(count)

#define ERROR(...) GLUE(ERROR_CHOOSE_HELPER(COUNT_ARGS_MAX5(__VA_ARGS__)),\
    (__VA_ARGS__))

#define ASSERT1(expr) singleArgumentExpansion(expr)
#define ASSERT2(expr, explain) twoArgumentExpansion(expr, explain)

#define ASSERT_CHOOSE_HELPER2(count) ASSERT##count
#define ASSERT_CHOOSE_HELPER1(count) ASSERT_CHOOSE_HELPER2(count)
#define ASSERT_CHOOSE_HELPER(count) ASSERT_CHOOSE_HELPER1(count)

#define ASSERT(...) GLUE(ASSERT_CHOOSE_HELPER(COUNT_ARGS_MAX5(__VA_ARGS__)),\
    (__VA_ARGS__))


void test_error(const std::string& s,...)
{
    //ASSERT("one"); // singleArgumentExpansion(one)
    //ASSERT("two", "foopy"); // twoArgumentExpansion(two, "foopy")

    ERROR("Only print a title");
    ERROR("Error title", "Extended error description");
}

这些方法各有优缺点,你应该根据你的具体需求来选择最合适的方法

文章来源:https://blog.csdn.net/weixin_39568531/article/details/134999318
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。