You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现可直接传入类型-整数对的可变参数C++模板?

实现简化的「类型-大小对」可变参数模板

问题描述

我正在编写一个接受可变参数「类型-大小对」的模板,每个对由类型和大小组成。目前已经实现了一个可用版本:

#include <cstddef>
#include <iostream>

template <typename T, size_t N>
class DeferPair
{
    using Type = T;
    static constexpr size_t kSize = N;

public:
    void defer(const T& e)
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
        //Defer this specific type into a fixed-length/statically-allocated queue
        //Discard oldest if the queue is already full
    }
};

template <typename... Pairs>
class Defer;

template <typename Pair, typename... RemainingPairs>
class Defer<Pair, RemainingPairs...> : public Pair, public Defer<RemainingPairs...>
{
public:
    using Pair::defer;
    using Defer<RemainingPairs...>::defer;
};

template <typename Pair>
class Defer<Pair> : public Pair
{
public:
    using Pair::defer; 
};

struct A {};
struct B {};
struct C {};

int main()
{
    Defer<DeferPair<A, 1>, DeferPair<B, 2>, DeferPair<C, 3>> d;

    d.defer(A{});
    d.defer(B{});
    d.defer(C{});

    return 0;
}

我希望简化使用语法,将当前写法:

Defer<DeferPair<A, 1>, DeferPair<B, 2>, DeferPair<C, 3>> d;

改为更简洁的形式:

Defer<A, 1, B, 2, C, 3> d;

想知道这是否可以实现,以及这种写法的可读性如何,有没有更优的实现方案。


一、可以实现,以下是具体方案

我们可以通过模板元编程对参数包进行分组处理,自动将连续的「类型+非类型参数」组装成DeferPair,直接适配简化后的语法。

直接修改Defer模板的实现(推荐)

这个方案无需额外辅助类,直接让Defer处理成对的参数,通过静态断言保证参数数量为偶数:

#include <cstddef>
#include <iostream>

template <typename T, size_t N>
class DeferPair
{
    using Type = T;
    static constexpr size_t kSize = N;

public:
    void defer(const T& e)
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
        // 将特定类型元素存入固定长度静态队列,满时丢弃最旧元素
    }
};

// 辅助实现类,处理递归参数包
template <typename...>
class DeferImpl;

// 递归终止:空参数包
template <>
class DeferImpl<> {};

// 递归处理每一对(类型+大小)参数
template <typename T, size_t N, typename... Rest>
class DeferImpl<T, N, Rest...> : public DeferPair<T, N>, public DeferImpl<Rest...>
{
public:
    // 引入所有基类的defer函数
    using DeferPair<T, N>::defer;
    using DeferImpl<Rest...>::defer;
};

// 对外暴露的简化版Defer模板
template <typename... Args>
class Defer : public DeferImpl<Args...>
{
    // 静态断言:参数必须成对出现
    static_assert(sizeof...(Args) % 2 == 0, "Defer template arguments must be in (Type, Size) pairs");
};

struct A {};
struct B {};
struct C {};

int main()
{
    // 使用简化语法
    Defer<A, 1, B, 2, C, 3> d;

    d.defer(A{});
    d.defer(B{});
    d.defer(C{});

    return 0;
}

编译运行后,输出结果和原版本完全一致,但使用语法更简洁。


二、可读性分析

  • 简化写法的优势:Defer<A,1,B,2,C,3>减少了重复的DeferPair声明,对于熟悉模板参数规则的开发者来说,可读性更高,能快速对应每个类型的大小配置。
  • 潜在的困惑:对模板元编程不熟悉的开发者可能会混淆参数类型(比如分不清哪个是类型、哪个是大小),但通过命名规范(如大小用数字字面量、类型用自定义结构体/类)可以有效缓解这个问题。
  • 原写法的价值:原写法Defer<DeferPair<A,1>,...>虽然冗长,但结构更明确,每个DeferPair清晰标记一个「类型-大小对」,对新手更友好。

三、更优实现方案

1. C++17+ 折叠表达式优化

如果使用C++17及以上,可以利用折叠表达式简化defer函数的引入,避免递归的using声明:

#include <cstddef>
#include <iostream>

template <typename T, size_t N>
class DeferPair
{
    using Type = T;
    static constexpr size_t kSize = N;

public:
    void defer(const T& e)
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
};

// 用折叠表达式一次性引入所有基类的defer
template <typename... Pairs>
class DeferImpl : public Pairs...
{
public:
    using Pairs::defer...;
};

// 辅助模板:将参数包组装成DeferPair序列
template <typename... Args>
struct DeferHelper;

template <typename T, size_t N, typename... Rest>
struct DeferHelper<T, N, Rest...>
{
    using type = DeferImpl<DeferPair<T, N>, typename DeferHelper<Rest...>::type>;
};

template <>
struct DeferHelper<>
{
    using type = DeferImpl<>;
};

// 对外暴露的Defer模板
template <typename... Args>
class Defer : public DeferHelper<Args...>::type
{
    static_assert(sizeof...(Args) % 2 == 0, "Defer requires (Type, Size) pairs");
};

struct A {};
struct B {};
struct C {};

int main()
{
    Defer<A,1,B,2,C,3> d;
    d.defer(A{});
    d.defer(B{});
    d.defer(C{});
    return 0;
}

2. 基于Tuple的无继承实现(C++20+)

如果不想使用多继承,可以用std::tuple存储所有DeferPair实例,通过C++20的auto参数和constexpr递归分发调用:

#include <cstddef>
#include <iostream>
#include <tuple>

template <typename T, size_t N>
class DeferPair
{
public:
    using Type = T;
    static constexpr size_t kSize = N;

    void defer(const T& e)
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
};

// 辅助模板:生成DeferPair的tuple类型
template <typename... Args>
struct DeferTupleBuilder;

template <typename T, size_t N, typename... Rest>
struct DeferTupleBuilder<T, N, Rest...>
{
    using type = decltype(std::tuple_cat(
        std::declval<std::tuple<DeferPair<T, N>>>(),
        std::declval<typename DeferTupleBuilder<Rest...>::type>()
    ));
};

template <>
struct DeferTupleBuilder<>
{
    using type = std::tuple<>;
};

template <typename... Args>
class Defer
{
    static_assert(sizeof...(Args) % 2 == 0, "Defer requires (Type, Size) pairs");
    using TupleType = typename DeferTupleBuilder<Args...>::type;
    TupleType pairs_;

    // 递归分发调用到对应的DeferPair
    template <size_t I = 0>
    void dispatch(const auto& e)
    {
        if constexpr (I < std::tuple_size_v<TupleType>)
        {
            using Pair = std::tuple_element_t<I, TupleType>;
            if constexpr (std::is_same_v<std::decay_t<decltype(e)>, typename Pair::Type>)
            {
                std::get<I>(pairs_).defer(e);
            }
            else
            {
                dispatch<I+1>(e);
            }
        }
    }

public:
    void defer(const auto& e)
    {
        dispatch(e);
    }
};

struct A {};
struct B {};
struct C {};

int main()
{
    Defer<A,1,B,2,C,3> d;
    d.defer(A{});
    d.defer(B{});
    d.defer(C{});
    return 0;
}

这个方案避免了多继承,代码结构更清晰,也更易于扩展新功能。


内容的提问来源于stack exchange,提问作者Patrick Wright

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 12:50:58