【问题标题】:How to write a variadic function without parameter pack?如何编写没有参数包的可变参数函数?
【发布时间】:2018-07-13 19:27:32
【问题描述】:

假设我有一个模板

template<typename Bar>
Result foo(const Input& input);

而我想创建一个模板foo(),它将获得许多Bar 模板参数和许多Input,并将Result 放在一起。

我写了一个带有输入容器的版本:

template<typename Bar>
Result foo(const std::vector<Input>& inputs);

template<typename Bar1, typename Bar2, typename ... Bars>
Result foo(const std::vector<Input>& inputs) {
    // calls foo<Bar2, Bars ...>(shorter_inputs);
}

但是,上面的版本无法在编译时检查输入的长度是否与模板参数的数量匹配。另外,我想要一个不需要容器的版本,只需输入即可:

foo<Bar1, Bar2>(input1, input2);
// instead of
// foo<Bar1, Bar2>({input1, input2});

我尝试写这样的东西

template<typename Bar1, typename Bar2, typename ... Bars>
Result bar(const Input& in1, const Input& in2, const Input& ... inputs)

被编译器拒绝,因为... 仅适用于参数包。不借助va_args,是否可以在C++中写出这样一个可变参数函数?

【问题讨论】:

    标签: c++ c++11 templates variadic-templates template-meta-programming


    【解决方案1】:

    只需使用两个参数包:

    template<typename Bar, typename... Bars, typename Input, typename... Inputs>
    std::string foo(const Input &input, const Inputs &... inputs)
    {
        std::string result(Bar::get_name());
        result.append(input);
    
        if constexpr (sizeof...(inputs) > 0)
            result.append(foo<Bars...>(inputs...));
    
        return result;
    }
    

    【讨论】:

    • 这看起来很有希望,但是当我尝试递归模板时,扩展的输入变成了条。您是否有指向我可以复制的示例的链接?
    • 为了简单起见,让我们假设每个 Bar 类都有一个静态 std::string get_name() 方法,并且 foo 连接名称,在它们之间插入一些字符串分隔符(输入)。跨度>
    • @user44168,我已经根据您在问题中提供的内容更新了示例。但是,它使用 C++17 if constexpr 功能来结束递归。可以用C++17还是只能用C++11?
    • @user44168,再次使用字符串连接更新。
    • 感谢您使用解决方案进行编辑!我的错误是将两个包都扩展为 。该项目在 C++14 中,所以我用 bar. 的单独定义结束了递归
    【解决方案2】:

    不确定你想要什么...

    如果ResultInput 是类型而不是模板参数,并且您想要一个模板foo() 接收与模板参数一样多的Input 对象,您可以从自定义模板参数GetFirst 开始选择可变参数列表中的第一个模板参数

    template <typename T0, typename ...>
    struct GetFirst
     { using type = T0; };
    

    并写foo()如下

    template <typename ... Bars>
    Result foo (typename GetFirst<Input, Bars>::type const & ... is)
     { 
       // something with is...
       return {};
     }
    

    所以你有

    //foo<long, long long>(Input{}, Input{}, Input{}); // compilation error
    
    foo<int, long, long long>(Input{}, Input{}, Input{}); // compiles
    
    //foo<int, long, long long>(Input{}, Input{}); // compilation error
    

    如果要递归管理单个is...,可以写foo()如下

    template <typename, typename ... Bars>
    Result foo (Input const & i0,
                typename GetFirst<Input, Bars>::type const & ... is)
     {
       // do something with i0
    
       return foo<Bars...>(is...);
     }
    

    但您还需要一个基本案例模板foo() 来终止递归;我提出了一个foo(),它接收一个具有默认值的模板非类型参数(当列表为空时拦截Bars...

    template <int = 0>
    Result foo ()
     { return {}; }
    

    以下是一个完整的,可能有点傻,但正在编译的示例

    struct Input  { };
    struct Result { };
    
    template <typename T0, typename ...>
    struct GetFirst
     { using type = T0; };
    
    // ground case
    template <int = 0>
    Result foo ()
     { return {}; }
    
    // recursive case
    template <typename, typename ... Bars>
    Result foo (Input const & i0,
                typename GetFirst<Input, Bars>::type const & ... is)
     {
       // do something with i0
    
       return foo<Bars...>(is...);
     }
    
    int main()
     {
       //foo<long, long long>(Input{}, Input{}, Input{}); // compilation error
    
       foo<int, long, long long>(Input{}, Input{}, Input{}); // compiles
    
       //foo<int, long, long long>(Input{}, Input{}); // compilation error
     }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-04-02
      • 2012-01-11
      • 2018-05-31
      • 2016-12-11
      • 2018-07-04
      • 2013-05-21
      • 1970-01-01
      相关资源
      最近更新 更多