订单独立的可变参数模板库专业化

时间:2017-11-07 14:17:31

标签: c++ templates recursion variadic-templates c++17

On GCC / Clang以下代码无法编译:

#include <iostream>
#include <type_traits>
using namespace std;

template<typename X, typename T, typename ...Ts>
void v(X& x, T& value, Ts&... args)
{
    v(x, value);
    v(x, args...);
}

template<typename X, typename T>
enable_if_t<is_integral_v<T>>
v(X& x, T& value) {
    cout << value << endl;
}

template<typename X, typename T>
enable_if_t<is_floating_point_v<T>>
v(X& x, T& value) {
    cout << value << endl;
}

int main() {
    float f = 5.5;
    int i = 2;
    v(f, i, i);
}

给出错误:

> prog.cc: In instantiation of 'void v(X&, T&, Ts& ...) [with X = float; T = int; Ts = {}]':
> prog.cc:8:3:   required from 'void v(X&, T&, Ts& ...) [with X = float; T = int; Ts = {int}]'
> prog.cc:27:14:   required from here
> prog.cc:9:3: error: no matching function for call to 'v(float&)'
>   v(x, args...);
>   ~^~~~~~~~~~~~
> prog.cc:6:6: note: candidate: template<class X, class T, class ... Ts> void v(X&, T&, Ts& ...)
>  void v(X& x, T& value, Ts&... args)
>       ^
> prog.cc:6:6: note:   template argument deduction/substitution failed:
> prog.cc:9:3: note:   candidate expects at least 2 arguments, 1 provided
>   v(x, args...);
>   ~^~~~~~~~~~~~

如果在定义了所有基函数特化之后定义了可变参数函数,那么代码就会编译,但是你会得到这个非常不受欢迎的约束,这会导致代码破坏,包含错误的顺序。

有没有办法以不依赖于定义顺序的方式向可变参数基函数添加特化?

1 个答案:

答案 0 :(得分:1)

我在你的代码中看到了一些问题。

第一个也是最重要的:你给同一个名称(v())赋予两个完全不同的东西;第一个v()在第一个参数上调用打印函数,并为以下参数递归调用。其他v()正在打印(选择SFINAE)功能。

建议:使用不同的名字;在以下示例中,我使用foo()作为递归函数,使用bar()作为打印函数。

第二:您的第一个参数(X & x)未使用。

建议:删除它。

第三:你的函数正在接收非常量引用,但它们不会修改值。

建议:接收参数作为const引用;所以你也可以调用v(5.5f, 3L)(你不能用非const引用调用这种方式)

根据这些建议,您的打印功能将变为

template <typename T>
std::enable_if_t<std::is_integral<T>{}> bar (T const & value)
 { std::cout << "integral case: " << value << std::endl; }

template <typename T>
std::enable_if_t<std::is_floating_point<T>{}> bar (T const & value)
 { std::cout << "floating case: " << value << std::endl; }

和递归函数(添加地面案例)

// ground case
void foo ()
 { }

// recursion case
template <typename T, typename ... Ts>
void foo (T const & value, Ts const & ... args)
 {
    bar(value);
    foo(args...);
 }

以下是一个完整的工作示例

#include <iostream>
#include <type_traits>

template <typename T>
std::enable_if_t<std::is_integral<T>{}> bar (T const & value)
 { std::cout << "integral case: " << value << std::endl; }

template <typename T>
std::enable_if_t<std::is_floating_point<T>{}> bar (T const & value)
 { std::cout << "floating case: " << value << std::endl; }

// ground case
void foo ()
 { }

// recursion case
template <typename T, typename ... Ts>
void foo (T const & value, Ts const & ... args)
 {
    bar(value);
    foo(args...);
 }

int main ()
 {
   float f {5.5};
   int i   {2};

   foo(f, i, i, 3L, 6.6f);
}

- 编辑 -

OP说

  

主要问题仍未解决 - bar之前必须定义foo

如果您接受bar成为结构中的静态方法,我建议使用以下bar结构

struct bar
 {
   template <typename T>
   static std::enable_if_t<std::is_integral<T>{}> func (T const & value)
    { std::cout << "integral case: " << value << std::endl; }

   template <typename T>
   static std::enable_if_t<std::is_floating_point<T>{}> func (T const & value)
    { std::cout << "floating case: " << value << std::endl; }
 };

foo成为

//地面案例

template <typename>
void foo ()
 { }

// recursion case
template <typename Bar, typename T, typename ... Ts>
void foo (T const & value, Ts const & ... args)
 {
   Bar::func(value);
   foo<Bar>(args...);
 }

并按以下方式调用

foo<bar>(f, i, i, 3L, 6.6f);

以下是一个完整的工作示例,bar

之后定义了foo()结构
#include <iostream>
#include <type_traits>

// ground case
template <typename>
void foo ()
 { }

// recursion case
template <typename Bar, typename T, typename ... Ts>
void foo (T const & value, Ts const & ... args)
 {
   Bar::func(value);
   foo<Bar>(args...);
 }

struct bar
 {
   template <typename T>
   static std::enable_if_t<std::is_integral<T>{}> func (T const & value)
    { std::cout << "integral case: " << value << std::endl; }

   template <typename T>
   static std::enable_if_t<std::is_floating_point<T>{}> func (T const & value)
    { std::cout << "floating case: " << value << std::endl; }
 };

int main ()
 {
   float f {5.5};
   int i   {2};

   foo<bar>(f, i, i, 3L, 6.6f);
}