其参数作为模板非类型传递的可变参数函数模板
Variadic function templates whose arguments are passed as template nontypes
C++17 标准提到了一个奇特的可变参数函数模板
- 不接受普通函数参数,而是
- 采用非类型模板参数,
喜欢f<200, 50, 6>() == 256
.
我想,这很奇怪,让我看看我是否可以自己编写代码。我的代码很优雅。然而,我优雅的代码无法编译,所以在两个毫无结果的小时后,我写了这个丑陋的代码:
#include <iostream>
namespace {
template<int A> constexpr int f() {return A;}
template<int A, int B, int... C> constexpr int f() {
if (sizeof...(C)) return A + f<B, C...>();
else return A + B;
}
}
int main() {
int n = f<200, 50, 6>();
std::cout << n << "\n";
return 0;
}
这个丑陋的代码有效,而且很有趣,因为它顺便教会了我 sizeof...()
。不过,这不可能是对的,对吗?
每次我尝试更简单的东西时,编译器都会犹豫,抱怨名称查找冲突或模板重新定义或其他类似的东西。
感觉我的观念不对。我怀疑我错过了重点。请问我错过了什么?
参考资料
供参考,在标准(草案here)中,引起我疑问的部分是sect。 5.13.8(第 3 和 4 段)。然而,据我所知,挑衅是偶然的。我的问题不是关于标准本身。我的问题是关于可变参数的正确、优雅的使用。
非编译示例
如果你想要我的更优雅的非编译代码示例,这里有一个:
namespace {
template<> constexpr int f() {return 0;}
template<int A, int... C> constexpr int f() {
return A + f<C...>();
}
}
阅读编译器的错误消息后,我明白了为什么这段代码会失败,所以这不是我的问题。相反,我的问题是
- 像
f<200, 50, 6>()
这样的可变参数调用应该如何正确实现并且
- 关于 C++ 可变参数,我缺少什么概念。
写函数的C++17方式是
template<auto... Vals>
constexpr auto sum() noexcept(noexcept((Vals + ...)))
{
return (Vals + ...);
}
其中 template<auto... Vals>
表示我们有一个可变值模板,而 (Vals + ...)
是一个 fold expression,它将所有值加在一起(需要外部 ()
制作一个有效的折叠表达式)。
我使用了auto...
,因此它不限于特定类型。可以添加 SFINAE 以将模板限制为仅支持加法的类型(如果需要的话),否则如果参数包中的所有类型都不能加在一起,您将收到编译器错误。
C++17 标准提到了一个奇特的可变参数函数模板
- 不接受普通函数参数,而是
- 采用非类型模板参数,
喜欢f<200, 50, 6>() == 256
.
我想,这很奇怪,让我看看我是否可以自己编写代码。我的代码很优雅。然而,我优雅的代码无法编译,所以在两个毫无结果的小时后,我写了这个丑陋的代码:
#include <iostream>
namespace {
template<int A> constexpr int f() {return A;}
template<int A, int B, int... C> constexpr int f() {
if (sizeof...(C)) return A + f<B, C...>();
else return A + B;
}
}
int main() {
int n = f<200, 50, 6>();
std::cout << n << "\n";
return 0;
}
这个丑陋的代码有效,而且很有趣,因为它顺便教会了我 sizeof...()
。不过,这不可能是对的,对吗?
每次我尝试更简单的东西时,编译器都会犹豫,抱怨名称查找冲突或模板重新定义或其他类似的东西。
感觉我的观念不对。我怀疑我错过了重点。请问我错过了什么?
参考资料
供参考,在标准(草案here)中,引起我疑问的部分是sect。 5.13.8(第 3 和 4 段)。然而,据我所知,挑衅是偶然的。我的问题不是关于标准本身。我的问题是关于可变参数的正确、优雅的使用。
非编译示例
如果你想要我的更优雅的非编译代码示例,这里有一个:
namespace {
template<> constexpr int f() {return 0;}
template<int A, int... C> constexpr int f() {
return A + f<C...>();
}
}
阅读编译器的错误消息后,我明白了为什么这段代码会失败,所以这不是我的问题。相反,我的问题是
- 像
f<200, 50, 6>()
这样的可变参数调用应该如何正确实现并且 - 关于 C++ 可变参数,我缺少什么概念。
写函数的C++17方式是
template<auto... Vals>
constexpr auto sum() noexcept(noexcept((Vals + ...)))
{
return (Vals + ...);
}
其中 template<auto... Vals>
表示我们有一个可变值模板,而 (Vals + ...)
是一个 fold expression,它将所有值加在一起(需要外部 ()
制作一个有效的折叠表达式)。
我使用了auto...
,因此它不限于特定类型。可以添加 SFINAE 以将模板限制为仅支持加法的类型(如果需要的话),否则如果参数包中的所有类型都不能加在一起,您将收到编译器错误。