【问题标题】:C++11 variadic templates: default index array valueC++11 可变参数模板:默认索引数组值
【发布时间】:2012-05-25 05:46:35
【问题描述】:

这是一个使用从Johannes Schaub - litbLuc Danton 的答案改编的代码打印元组的简短程序。

#include <iostream>
#include <tuple>

template<int ...>
struct seq { };

template<int N, int ...S>
struct gens : gens<N-1, N-1, S...> { };

template<int ...S>
struct gens<0, S...> {
  typedef seq<S...> type;
};

template <int ...S, typename ...T>
void print(const std::tuple<T...> & tup, seq<S...> s) {
  int res[] = { (std::cout << std::get<S>(tup) << " ", 0)... };
  std::cout << std::endl;
}

int main() {
  std::tuple<double, int, char> tup(1.5, 100, 'c');
  print(tup, gens<std::tuple_size<decltype(tup)>::value >::type());
  return 0;
}

print 的第二个参数总是gens&lt;N&gt;::type(),其中N 是元组的大小。我试图通过提供默认参数来避开要打印的第二个参数:

template <int ...S, typename ...T>
void print(const std::tuple<T...> & tup, seq<S...> s = gens<std::tuple_size<decltype(tup)>::value >::type()) {
  int res[] = { (std::cout << std::get<S>(tup) << " ", 0)... };
  std::cout << std::endl;
}

但是,结果是编译器错误:

tmp5.cpp: 在函数中'void print(const std::tuple<_elements ...>&, seq) [with int ...S = {}; T = {double, int, char}]':
tmp5.cpp:23:12:错误:嵌套名称说明符中使用的类型“std::tuple_size&>”不完整

你知道有什么方法可以在没有第二个参数的情况下提供S... 给像print 这样的函数吗?

【问题讨论】:

    标签: c++ c++11 tuples variadic-templates


    【解决方案1】:

    不,没有。

    事实上,这个问题并不局限于可变参数模板,它发生在所有模板函数中:参数的模板类型不能从其默认值推导出来。

    template <typename T>
    void func(T = 0) {} // expected-note {candidate template ignored:\
                                          couldn't infer template argument 'T'}
    
    int main() {
      func(); // expected-error {no matching function for call to 'func'}
    }
    

    你需要换档。

    最简单的方法是提供一个重载,负责传递第二个参数。毕竟,默认参数只是语法糖,以避免编写转发函数。

    【讨论】:

    • clang 风格的 cmets 真的很书呆子。
    • @JohannesSchaub-litb:我必须承认我非常喜欢这种呈现编译错误的方式(当只有几个错误时......)
    • 太糟糕了。我正在使用异构元组(tuple&lt;array&lt;double, N1&gt;, array&lt;double, N2&gt;, ...&gt;,其内容都可以发送到模板函数template &lt;int N&gt; array&lt;double, N&gt; f(array&lt;double, N&gt;) 以生成新元组。我想现在,我会将序列设置为常量表达式以避免在第二个参数中重复 gens 代码。
    【解决方案2】:

    问题是如果不提供第二个函数参数,编译器无法推导出索引序列S...。当它到达默认参数时,它需要知道S... 是什么,所以它不能使用默认参数来确定它。

    这可以通过提供print 的重载来构建索引列表并转发到接受索引列表的重载来解决:

    template <typename ...T>
    void print(const std::tuple<T...> & tup) {
      print(tup,typename gens<sizeof...(T)>::type());
    }
    

    【讨论】:

    • 我明白你的意思。但是有没有办法为可变参数模板分配默认参数?我将制作其中的几个函数,并希望尽可能简单地声明和调用它们......
    • 没有为可变参数提供默认参数的语法。您能做的最好的事情就是提供专业化或重载。
    【解决方案3】:

    可能有更好的方法,但我能想到的最简单的方法是添加额外的间接级别:

    #include <iostream>
    #include <tuple>
    
    template<int ...>
    struct seq { };
    
    template<int N, int ...S>
    struct gens : gens<N-1, N-1, S...> { };
    
    template<int ...S>
    struct gens<0, S...> {
      typedef seq<S...> type;
    };
    
    template <typename ...T, int ...S>
    void print_impl(const std::tuple<T...> & tup, seq<S...>) {
      int res[] = { (std::cout << std::get<S>(tup) << " ", 0)... };
      std::cout << std::endl;
    }
    // Pass args to real implementation here
    template <typename ...T>
    void print(const std::tuple<T...> & tup)
    {
        print_impl(tup, typename gens<sizeof...(T)>::type());
    }
    
    int main() {
      std::tuple<double, int, char> tup(1.5, 100, 'c');
      print(tup);
      return 0;
    }
    

    【讨论】:

    • 我同意,但这与提供打印序列一样困难......我想知道是否有办法为可变参数类型提供默认参数?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-12
    • 2012-12-21
    • 1970-01-01
    • 2016-10-06
    相关资源
    最近更新 更多