3

这与我之前的帖子有关。我想知道为什么一个尝试的解决方案不起作用。

template <typename... T>             /* A */
size_t num_args ();

template <>
size_t num_args <> ()
{
    return 0;
}

template <typename H, typename... T> /* B */
size_t num_args ()
{
    return 1 + num_args <T...> ();
}

例如,如果我尝试调用,num_args<int,float>()那么错误是函数调用不明确:

  • A 与 T={int,float}
  • B 与 H=int, T={float}

我不明白这是怎么模棱两可的——A是一个声明,B是A声明的函数的定义。对吧?

我正在尝试使这个示例有效,而对我之前的问题的回答似乎声称它永远不会有效。

如果是这样,可变参数自由函数的意义何在?他们能做什么

4

3 回答 3

7

我不明白这是怎么模棱两可的——A是一个声明,B是A声明的函数的定义。对吧?

不是。A 是函数模板的声明,B 是另一个函数模板的声明(和定义)。

编译器无法在两者之间做出决定:它们都没有参数,并且模板参数是两者的匹配项。

中间的一个是在 A 中声明的函数模板的显式全面特化。

如果你试图让 B 成为 A 的另一个特化:

template <typename H, typename... T> /* B */
size_t num_args<H, T...>()
{
    return 1 + num_args <T...> ();
}

...你最终会得到一个函数模板的部分特化,这是不允许的。

您可以通过使用具有部分特化的类模板和调用类模板的函数模板的常用技巧来做到这一点:

template <typename... T>
class Num_Args;

template <>
struct Num_Args <>
{
    static constexpr size_t calculate() {
        return 0;
    }
};

template <typename H, typename... T>
struct Num_Args <H, T...>
{
    static constexpr size_t calculate() {
        return 1 + Num_Args<T...>::calculate();
    }
};

template <typename... T> /* B */
constexpr size_t num_args ()
{
    return Num_Args<T...>::calculate();
}
于 2011-08-18T15:53:36.770 回答
4

关于免费可变参数函数模板的有用性/无用性:这些通常的用例是有一个可变参数函数参数列表,在这种情况下,空情况的常规重载就可以了:

size_t num_args()
{
    return 0;
}

template <typename H, typename... T> /* B */
size_t num_args (H h, T... t)
{
    return 1 + num_args(t...);
}


编辑:

据我所知,以下滥用enable_if应该可以解决您最初的问题:

#include <utility>

// Only select this overload in the empty case 
template <typename... T>
typename std::enable_if<(sizeof...(T) == 0), size_t>::type
num_args() 
{ 
    return 0;
}

template <typename H, typename... T>
size_t
num_args() 
{
    return 1 + num_args<T...>();
}

(Edit2:颠倒重载的顺序以使代码实际编译)

于 2011-08-19T09:16:34.567 回答
0

虽然我真的很喜欢std::enable_if<sizeof...(T) == 0, _>JohannesD 的 hack,但我仍然会放弃下面的 hack,我实际上不记得我从哪里学到的,它只使用免费函数解决了歧义,而不必使用类:

template <typename One>
size_t num_args() {
    return 1;
}

template <typename First, typename Next, typename... Rest>
size_t num_args() {
    return 1 + num_args<Next, Rest...>();
}

不需要部分特化,它通过在一个参数处结束解包递归,完全避免了零参数的情况。此外,请参阅https://stackoverflow.com/a/58295007/1525238以获得使用相同想法的更好的 c++17 解决方案。

唯一的问题是它实际上没有涵盖零模板参数的情况,这在自由函数 AFAIK 的上下文中是不可能的。如果从消费者的角度需要,则必须使用部分专业化的类,请参阅 R. Martinho Fernandes 的回答。

至于可变参数模板自由函数的实用程序,我发现它们作为constexpr类型安全的累加计算器实用程序特别有用。

于 2021-11-18T14:34:50.660 回答