其参数作为模板非类型传递的可变参数函数模板

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

阅读编译器的错误消息后,我明白了为什么这段代码会失败,所以这不是我的问题。相反,我的问题是

写函数的C++17方式是

template<auto... Vals>
constexpr auto sum() noexcept(noexcept((Vals + ...)))
{
    return (Vals + ...);
}

其中 template<auto... Vals> 表示我们有一个可变值模板,而 (Vals + ...) 是一个 fold expression,它将所有值加在一起(需要外部 ()制作一个有效的折叠表达式)。

我使用了auto...,因此它不限于特定类型。可以添加 SFINAE 以将模板限制为仅支持加法的类型(如果需要的话),否则如果参数包中的所有类型都不能加在一起,您将收到编译器错误。