C++11 中的非类型可变参数函数模板

Posted

技术标签:

【中文标题】C++11 中的非类型可变参数函数模板【英文标题】:Non-type variadic function templates in C++11 【发布时间】:2011-12-14 04:56:42 【问题描述】:

我看到了一个blog post,它使用了非类型可变参数模板(gcc 目前不支持,只有 clang 支持)。

template <class T, size_t... Dimensions>
struct MultiDimArray  /* ... */ ;

帖子中的示例编译良好,但我无法让它与函数模板一起使用。

谁能帮忙找出正确的语法(如果存在的话)?

int max(int n)  return n;  // end condition

template <int... N> // replacing int... with typename... works
int max(int n, N... rest) // !! error: unknown type name 'N'

    int tmp = max(rest...);
    return n < tmp? tmp : n;


#include <iostream>
int main() 

   std::cout << max(3, 1, 4, 2, 5, 0) << std::endl;   

【问题讨论】:

我认为标准库中已经存在可变参数max...但我不完全确定。 @Kerrek max 不是重点,只是一个例子。 你想要一个非编译时函数(即max(3,1,4,foo(),5,bar())而不是max&lt;3,1,4,2,5,6&gt;()),它接受任意数量的参数,所有同一类型?哪个返回最大值? @AaronMcDaid,是的,我正在寻找一个非编译时函数。多亏了 Konrad 和 Luc 的解释,我已经看到了我的方式的错误。 等等,为什么你说 gcc 不支持这个?使用 4.9 编译良好​​span> 【参考方案1】:

您只是混淆了类型名称和非类型名称。你想要的根本行不通。

可以可能在函数中使用可变参数非类型模板,但不能作为(非模板)参数:

template <int N, int... Rest>
int max()

    int tmp = max<Rest...>();
    return N < tmp ? tmp : N;

std::cout << max<3, 1, 4, 2, 5, 0>() << std::endl;

...虽然我没有对此进行测试并且我不确定它应该如何工作,因为您需要将部分专业化作为基本案例。你可以通过分派到一个部分专用的结构来解决这个问题:

template <int N, int... Rest>
struct max_t 
    static int const value = max_t<Rest...>::value > N ? max_t<Rest...>::value : N;
;

template <int N>
struct max_t<N> 
    static int const value = N;
;


template <int... NS>
int max()

    return max_t<NS...>::value;

这会起作用。

【讨论】:

我不是在寻找编译时函数。通过使用typename... N,我们得到了相同的行为,因为参数最终被强制转换为intmax 的任何重载的第一个参数是int)但它(IMO)有点不那么优雅,我想指定此函数采用任意数量的特定类型。一个可以使用initializer_list,但调用站点必须使用大括号。 @Motti 就像我的第一段所说,你想要的根本行不通。 @Motti template&lt;int I&gt; void foo(I i); 是荒谬的。你需要在变通之前了解这一点。 @Motti:如果您不是在寻找编译时函数,那么您也不是在寻找非类型模板参数。非类型模板参数必须是编译时常量表达式。 @LucDanton 当你这样说时,很明显,可变参数模板正在扰乱我的直觉。显然我还没有了解它们。【参考方案2】:

这将打印出所有元素, get max 可以类似地实现

template <int N>
void foo()
  cout << N << endl;


template <int N, int M, int ... Rest>
void foo()
  cout << N << endl;
  foo<M, Rest...>();



int main()
  foo<1, 5, 7>();

  return 0;

【讨论】:

我喜欢你避免歧义的方式。 @parker0203 为什么主模板需要三个模板参数?我认为template &lt;int N, int ... Rest&gt;foo() foo&lt;Rest...&gt;(); 就足够了。 这还不够,这就是为什么 M、N、rest... 都是必需的,MSVC 2017 在使用 foo(); 调用时会吐出错误;错误 C2668: 'foo': 对重载函数的模糊调用可能是 'void foo(void)' 或 'void foo(void)'【参考方案3】:

这里有两种定义可变参数函数模板的方法,它只接受int 参数。第一个在实例化时产生硬错误,第二个使用 SFINAE:

template<typename... T>
struct and_: std::true_type ;

template<typename First, typename... Rest>
struct and_
: std::integral_constant<
    bool
    , First::value && and_<Rest...>::value
> ;

template<typename... T>
void
foo(T... t)

    static_assert(
        and_<std::is_same<T, int>...>::value
        , "Invalid parameter was passed" );
    // ...


template<
    typename... T
    , typename = typename std::enable_if<
        and_<std::is_same<T, int>...>::value
    >::type
>
void
foo(T... t)

    // ...

如您所见,这里没有使用非类型模板参数。

【讨论】:

非常有趣的模板用法。可能也很少有应用。 :) 好主意,但这不适用于任何版本的 GCC。 > 错误:使用 2 个模板参数重新声明 > > 注意:先前的声明 'template struct and_' 使用了 1 。 godbolt.org/g/cQDSA0【参考方案4】:

Luc Danton 的解决方案无法正确处理非 int 类型的参数,但可以隐式转换为 int。这是一个:

template<typename T, typename U> struct first_type  typedef T type; ;
template<typename T> int max_checked(T n)  return n; 
template<typename T1, typename T2, typename ...Ts>
int max_checked(T1 n1, T2 n2, Ts ...ns)

  int maxRest = max_checked(n2, ns...);
  return n1 > maxRest ? n1 : maxRest;

template<typename ...T> auto max(T &&...t) ->
  decltype(max_checked<typename first_type<int, T>::type...>(t...))

  return max_checked<typename first_type<int, T>::type...>(t...);


struct S  operator int()  return 3;  ;
int v = max(1, 2.0, S()); // v = 3.

这里,max 将所有未更改的参数转发到 max_checked,它采用相同数量的 int 类型的参数(通过对 first_type 模板执行打包扩展来提供)。 decltype(...) 返回类型用于在任何参数无法转换为 int 时应用 SFINAE。

【讨论】:

对 Max_checked 的调用在 c++11 的部分排序中不明确。【参考方案5】:

以下是在 max 示例中实现可变参数 args 的方法,它可以采用任意数量的算术参数:

template<typename T, typename ... Ts>
struct are_arithmetic
    enum 
        value = std::is_arithmetic<T>::value && are_arithmetic<Ts...>::value
    ;
;

template<typename T>
struct are_arithmetic<T>
    enum 
        value = std::is_arithmetic<T>::value
    ;
;

template<typename Arg, typename = typename std::enable_if<std::is_arithmetic<Arg>::value>::type>
Arg max(Arg arg)
    return arg;


template<typename Arg, typename Arg1, typename ... Args, typename = typename std::enable_if<are_arithmetic<Arg, Arg1, Args...>::value>::type>
auto max(Arg arg, Arg1 arg1, Args ... args)
    auto max_rest = max(arg1, args...);
    return arg > max_rest ? arg : max_rest;


int main()
    auto res = max(1.0, 2, 3.0f, 5, 7l);

这很好,因为它可以采用任何数字类型,并且会按其原始类型返回最大数字,而不仅仅是 int

【讨论】:

当我运行这个时,res 的类型是 double 而不是 long。无论如何,我试图理解非类型模板参数,所以虽然你的解决方案很酷,但它并不是我想要的。不管怎么说,还是要谢谢你。 ideone.com/gUnoLp

以上是关于C++11 中的非类型可变参数函数模板的主要内容,如果未能解决你的问题,请参考以下文章

C++11新特性:9—— C++11在函数模板和类模板中使用可变参数

C++11 ——— 可变参数模板

C++11 ——— 可变参数模板

[C++11 模板的改进] --- 可变参数模板

C++中的可变参数模板

C++11:可变参数模板函数参数的数量?