订单无关的可变模板基础专业化

Order independent variadic template base specialization

本文关键字:专业化 单无关      更新时间:2023-10-16

在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...);
>   ~^~~~~~~~~~~~

如果变量函数是在定义了所有基本函数专门化之后定义的,则代码会编译,但随后会得到这种非常不理想的约束,这种约束会使代码中断,从而以错误的顺序包含内容。

有没有一种方法可以在不依赖于定义顺序的情况下为变基数函数添加专门化?

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

第一个也是最冒名顶替的:为做两件完全不同的事情的函数提供相同的名称(v());第一个CCD_ 2在第一个参数上调用打印函数。其他v()是打印(选择SFINAE)功能。

建议:使用不同的名称;在下面的示例中,我将foo()用于递归函数,将bar()用于打印函数。

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

建议:将其移除。

第三:函数接收的不是const引用,但它们不会修改值。

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

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

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说

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

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

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; }
};

CCD_ 12变为

//地面情况

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);
}