是否可以根据模板参数有条件地删除函数参数?

Can you conditionally remove a function parameter based on a template parameter?

提问人:fetis 提问时间:9/28/2023 最后编辑:UpAndAdamfetis 更新时间:9/28/2023 访问量:118

问:

我的函数看起来像这样

template<bool extra>
void func(int& arg1, const int arg2){
    //a lot of code...
    if (extra && arg2 > 0) ++arg1;
    arg1 *= 10;
    //a lot of code...
}

问题是 when is ,不使用,但函数仍然需要指定它,从而导致不必要的内存分配(您可以在此处检查程序集输出)。有没有办法修改函数,以便在这种情况下,何时是,只需要,避免内存分配?extrafalsearg2arg2extrafalsearg1arg2

C++ 函数模板

评论

1赞 tkausl 9/28/2023
专业化
0赞 Sam Varshavchik 9/28/2023
您刚才描述了模板专用化。这是一个高级主题,无法用 Stackoverflow 上的一两句话完全描述。请参阅您的 C++ 教科书,了解它是什么、它是如何工作的以及如何使用它的完整描述。
3赞 Jarod42 9/28/2023
只有 2 个功能? ?func(int& arg1);func_extra(int arg1, int arg2)
1赞 Jarod42 9/28/2023
像往常一样,仍然可以放在一个函数中,以避免复制粘贴。// a lot of code...
0赞 Phil1970 9/28/2023
有很多方法可以解决这个问题,但鉴于你的代码是抽象的(我们不知道它做了什么),在这种情况下,我们无法真正说出最佳解决方案。如上所述,可以简单地将默认值 0 用于 arg2: 。如果这还不够,那么下一个解决方案是 Jarod42 建议的 2 个函数。void func(int &arg1, const int arg2);

答:

3赞 ChrisB 9/28/2023 #1

这类问题通常使用模板专用化来解决, 不幸的是,这通常需要一个包装结构而不是普通函数。

下面是一个示例:

template<bool extra> // default case, only used for extra == false
struct Foo
{
    static void run(int arg1) {
        static_assert(extra == false);
        //...
    }
};
 
template<> //specialization, used for extra == true
struct Foo<true> 
{
    static void run(int arg1, const int arg2) {
        //...
    }
};

int main(){
    Foo<false>::run(1);
    Foo<true>::run(2, 3);
}

根据所需的 API,您有时可以创建一个包装函数或使用来避免稍微丑陋的调用。operator()::run


在您的特定示例中,如果两个函数版本的签名不同(因为您不希望出现这种情况),您甚至可以摆脱基本函数重载arg2extra == true

// In this simple case you could even remove the template argument completely 
// and make the distinction purely based on the number of arguments.
// It all depends on what API you want to provide.
template<bool extra>
void func(int& arg1){
    static_assert(extra == false);
}
template<bool extra>
void func(int& arg1, const int arg2){
    static_assert(extra == true);
}

注意:如果模板参数应保留,则C++ 20 requires 子句enable_if保护而不是 s 将更准确地表达函数签名的意图,目前代价是错误消息更糟糕static_assert

评论

1赞 fetis 9/28/2023
老实说,在这种情况下,结构模板专业化看起来很丑陋,但是函数重载是一个完美的解决方案,我不知道为什么我自己没有想出它,谢谢你这么详细的回答!
0赞 Jan Schultke 9/28/2023
如果您使用 requires-clause 而不是 static_assert,它可能会更干净。
0赞 Ben Voigt 9/28/2023
或者 SFINAE(如果您的工具链不够新,无法拥有)requires
0赞 ChrisB 9/28/2023
@JanSchultke,@BenVoigt:以下是所有三种情况的比较错误消息: godbolt.org/z/6Pjbz4s9Y 即使没有为 s 提供明确的消息,在我看来,它们已经是最具可读性的选项。过载解决总是会导致混乱的错误。吻。我知道拥有没有无效状态的函数签名感觉更好,但实际上第 1 行中的 a 之间几乎没有区别(除非我们必须防止模棱两可的重载)。因此,兼容性+可读性为我决定。static_assertrequiresstatic_assert
1赞 ChrisB 9/28/2023
@JanSchultke:所有三条错误消息都包含对调用站点和被调用函数的引用。但我在理论/语义层面上同意你的看法,所以我:)添加了一个注释。