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

如何在CRTP派生实例序列上调用模板函数?求无Lambda的简便方案

问题

现有一个接受符合CRTP基类概念/接口的泛型参数的模板函数:

template <typename Parser_T>
void print(Parser_T&& parser)

我希望遍历一系列CRTP派生实例来调用该函数。目前的实现是使用包含所有已知子类的std::variant,结合std::visit与Lambda来调用模板函数。

请问是否有更简便的实现方式,尤其是可以去掉Lambda的方案?我不需要同构存储实例,基于模板技巧的列表也可,但必须能指定实例的调用顺序。

示例代码

#include <iostream>
#include <array>
#include <variant>
#include <string>

template <class T>
struct parser_base
{
    std::string parse()
    {
        return static_cast<T*>(this)->parse_impl();
    }
};

struct parser_a : parser_base<parser_a>
{
    std::string parse_impl() {
        return "A\n";
    }

};

struct parser_b : parser_base<parser_b>
{
    std::string parse_impl() {
        return "B\n";
    }
};

template <typename Parser_T>
void print(Parser_T&& parser)  {
    std::cout << parser.parse();
}

int main() {
    using var_t = std::variant<parser_a, parser_b>;
    std::array<var_t,2> parser_list{parser_a{}, parser_b{}};

    for(auto &parser: parser_list) {
        std::visit([](auto &&arg) {
            print(arg);
        }, parser);
    }
}

解决方案

方法1:模板参数包直接展开(最简洁,无需variant/Lambda)

如果能在编译期确定要调用的实例类型和顺序,直接用模板参数包展开是最优解——完全不需要Lambda或variant,编译期处理效率最高:

#include <iostream>
#include <string>

// 原CRTP基类和派生类保持不变
template <class T>
struct parser_base
{
    std::string parse()
    {
        return static_cast<T*>(this)->parse_impl();
    }
};

struct parser_a : parser_base<parser_a>
{
    std::string parse_impl() {
        return "A\n";
    }
};

struct parser_b : parser_base<parser_b>
{
    std::string parse_impl() {
        return "B\n";
    }
};

template <typename Parser_T>
void print(Parser_T&& parser)  {
    std::cout << parser.parse();
}

// 模板函数:遍历参数包中的每个实例并调用print
template <typename... Parsers>
void print_all(Parsers&&... parsers) {
    (print(std::forward<Parsers>(parsers)), ...);
}

int main() {
    // 直接按指定顺序传入实例,编译期自动展开调用
    print_all(parser_a{}, parser_b{});
}

方法2:去掉Lambda的std::variant方案

如果必须保留variant存储,可以用自定义visitor替代Lambda,彻底去掉匿名函数:

#include <iostream>
#include <array>
#include <variant>
#include <string>

// 原CRTP基类、派生类、print函数保持不变
template <class T>
struct parser_base
{
    std::string parse()
    {
        return static_cast<T*>(this)->parse_impl();
    }
};

struct parser_a : parser_base<parser_a>
{
    std::string parse_impl() {
        return "A\n";
    }
};

struct parser_b : parser_base<parser_b>
{
    std::string parse_impl() {
        return "B\n";
    }
};

template <typename Parser_T>
void print(Parser_T&& parser)  {
    std::cout << parser.parse();
}

// 自定义visitor,替代Lambda
struct print_visitor {
    template <typename Parser_T>
    void operator()(Parser_T&& parser) const {
        print(std::forward<Parser_T>(parser));
    }
};

int main() {
    using var_t = std::variant<parser_a, parser_b>;
    std::array<var_t,2> parser_list{parser_a{}, parser_b{}};

    for(auto &parser: parser_list) {
        // 直接传入visitor实例,无需Lambda
        std::visit(print_visitor{}, parser);
    }
}

方法3:用std::tuple存储实例并遍历

如果需要存储实例但不想用variant,可以用tuple存储不同类型的实例,编译期遍历调用:

#include <iostream>
#include <tuple>
#include <string>

// 原CRTP基类、派生类、print函数保持不变
template <class T>
struct parser_base
{
    std::string parse()
    {
        return static_cast<T*>(this)->parse_impl();
    }
};

struct parser_a : parser_base<parser_a>
{
    std::string parse_impl() {
        return "A\n";
    }
};

struct parser_b : parser_base<parser_b>
{
    std::string parse_impl() {
        return "B\n";
    }
};

template <typename Parser_T>
void print(Parser_T&& parser)  {
    std::cout << parser.parse();
}

// 遍历tuple的辅助实现
template <typename Tuple, std::size_t... I>
void print_tuple_impl(const Tuple& tuple, std::index_sequence<I...>) {
    (print(std::get<I>(tuple)), ...);
}

template <typename Tuple>
void print_tuple(const Tuple& tuple) {
    print_tuple_impl(tuple, std::make_index_sequence<std::tuple_size_v<Tuple>>{});
}

int main() {
    // 按指定顺序存储不同类型的实例
    auto parsers = std::make_tuple(parser_a{}, parser_b{});
    print_tuple(parsers);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 15:45:29