使用c ++ 11重复代码

plo*_*gue 80 c++ templates c++11

我目前正在开展一个项目,我有以下问题.

我有一个C++方法,我想以两种不同的方式工作:

void MyFunction()
{
  foo();
  bar();
  foobar();
}

void MyFunctionWithABonus()
{
  foo();
  bar();
  doBonusStuff();
  foobar();
}
Run Code Online (Sandbox Code Playgroud)

我不想复制我的代码,因为实际的功能要长得多.问题是,在调用MyFunction而不是MyFunctionWithABonus时,我不能在任何情况下向程序添加执行时间.这就是为什么我不能只有一个布尔参数,我用C++比较检查.

我的想法是使用C++模板来虚拟复制我的代码,但我想不出一种方法,我没有额外的执行时间,我不必复制代码.

我不是模板专家,所以我可能会遗漏一些东西.

你们有没有想法?或者这在C++ 11中是不可能的?

Gib*_*bet 128

这样的事情会做得很好:

template<bool bonus = false>
void MyFunction()
{
  foo();
  bar();
  if (bonus) { doBonusStuff(); }
  foobar();
}
Run Code Online (Sandbox Code Playgroud)

通过以下方式呼叫

MyFunction<true>();
MyFunction<false>();
MyFunction(); // Call myFunction with the false template by default
Run Code Online (Sandbox Code Playgroud)

通过向函数添加一些不错的包装器,可以避免"丑陋"模板:

void MyFunctionAlone() { MyFunction<false>(); }
void MyFunctionBonus() { MyFunction<true>(); }
Run Code Online (Sandbox Code Playgroud)

你可以在那里找到关于那种技术的一些很好的信息.这是一篇"旧"论文,但这项技术本身就完全正确.

如果您可以访问一个不错的C++ 17编译器,您甚至可以使用constexpr if进一步推动该技术,如下所示:

template <int bonus>
auto MyFunction() {
  foo();
  bar();
  if      constexpr (bonus == 0) { doBonusStuff1(); }
  else if constexpr (bonus == 1) { doBonusStuff2(); }
  else if constexpr (bonus == 2) { doBonusStuff3(); }
  else if constexpr (bonus == 3) { doBonusStuff4(); }
  // Guarantee that this function will not compile
  // if a bonus different than 0,1,2,3 is passer
  else { static_assert(false);}, 
  foorbar();
}
Run Code Online (Sandbox Code Playgroud)

  • 并且[在C++ 17](https://wandbox.org/permlink/0NW2N0nvuZNKWOeG)`if constexpr(bonus){doBonusStuff(); }`. (21认同)
  • @Gibet:如果调用`doBonusStuff()`在非奖励的情况下由于某种原因甚至无法编译,那么它将产生巨大的差异. (13认同)
  • 并且编译器将很好地优化该检查 (11认同)
  • @ChrisDrew不确定constexpr是否会在这里添加任何内容.是吗? (5认同)
  • @WorldSEnder是的你可以,如果通过枚举或枚举类你的意思是constexpr(奖金== MyBonus :: ExtraSpeed). (4认同)
  • @Michael有一个小而重要的原因.最重要的原因是,如果你有任何回报(不是这里的情况).在C++ 11中处理它的唯一方法是使用一些模板特化.在这里你不会,这是非常巨大的.原因很简单,如果你在myFunction中传递一些东西,模板化的东西:template <class T,int bonus> MyFunction(T t){if(/*t is object*/){t.function();}}如果你尝试使用"int",甚至不会编译...不使用constexpr,如果哪个将丢弃int上的if.这也是非常巨大的.因为再一次禁止专业化. (2认同)

Jar*_*d42 55

使用模板和lambda,您可以:

template <typename F>
void common(F f)
{
  foo();
  bar();
  f();
  foobar();
}

void MyFunction()
{
    common([](){});
}

void MyFunctionWithABonus()
{
  common(&doBonusStuff);
}
Run Code Online (Sandbox Code Playgroud)

或者你可以创建prefixsuffix运作.

void prefix()
{
  foo();
  bar();
}

void suffix()
{
    foobar();
}

void MyFunction()
{
    prefix();
    suffix();
}

void MyFunctionWithABonus()
{
    prefix();
    doBonusStuff();
    suffix();
}
Run Code Online (Sandbox Code Playgroud)

  • 我实际上更喜欢这两个解决方案而不是布尔参数(模板或其他),而不管任何执行时间优势.我不喜欢布尔参数. (12认同)
  • 如果定义可见,编译器可能会内联代码并生成与为原始代码生成的代码相同的代码. (10认同)
  • 根据我的理解,由于额外的函数调用,第二个解决方案将有额外的运行时 这是第一个的情况吗?我不确定lambdas在这种情况下是如何工作的 (2认同)
  • 当您想要优化递归案例时,尾调用优化通常很重要.在这种情况下,简单内联......完成您需要的一切. (2认同)

Cor*_*lks 27

鉴于OP在调试方面做出的一些评论,这里是一个需要doBonusStuff()调试版本的版本,而不是发布版本(定义NDEBUG):

#if defined(NDEBUG)
#define DEBUG(x)
#else
#define DEBUG(x) x
#endif

void MyFunctionWithABonus()
{
  foo();
  bar();
  DEBUG(doBonusStuff());
  foobar();
}
Run Code Online (Sandbox Code Playgroud)

如果要检查条件,也可以使用assert宏,如果是错误则可以使用(但仅适用于调试版本;发布版本不会执行检查).

如果doBonusStuff()有副作用,请小心,因为这些副作用在发布版本中不会出现,并且可能会使代码中的假设无效.


Chr*_*rew 18

以下是Jarod42使用可变参数模板的答案的略微变化,因此调用者可以提供零或一个奖励功能:

void callBonus() {}

template<typename F>
void callBonus(F&& f) { f(); }

template <typename ...F>
void MyFunction(F&&... f)
{
  foo();
  bar();
  callBonus(std::forward<F>(f)...);
  foobar();
}
Run Code Online (Sandbox Code Playgroud)

来电代码:

MyFunction();
MyFunction(&doBonusStuff);
Run Code Online (Sandbox Code Playgroud)


Seb*_*ern 11

另一个版本,只使用模板而没有重定向功能,因为你说你不想要任何运行时开销.就我而言,这只会增加编译时间:

#include <iostream>

using namespace std;

void foo() { cout << "foo\n"; };
void bar() { cout << "bar\n"; };
void bak() { cout << "bak\n"; };

template <bool = false>
void bonus() {};

template <>
void bonus<true>()
{
    cout << "Doing bonus\n";
};

template <bool withBonus = false>
void MyFunc()
{
    foo();
    bar();
    bonus<withBonus>();
    bak();
}

int main(int argc, const char* argv[])
{
    MyFunc();
    cout << "\n";
    MyFunc<true>();
}

output:
foo
bar
bak

foo
bar
Doing bonus
bak
Run Code Online (Sandbox Code Playgroud)

现在只有一个版本MyFunc()bool参数作为模板参数.

  • @plougue模板是隐式内联的,并且内联的空函数不执行任何操作,可以由编译器消除. (6认同)

Ap3*_*p31 8

您可以使用标签调度和简单函数重载:

struct Tag_EnableBonus {};
struct Tag_DisableBonus {};

void doBonusStuff(Tag_DisableBonus) {}

void doBonusStuff(Tag_EnableBonus)
{
    //Do bonus stuff here
}

template<class Tag> MyFunction(Tag bonus_tag)
{
   foo();
   bar();
   doBonusStuff(bonus_tag);
   foobar();
}
Run Code Online (Sandbox Code Playgroud)

这很容易阅读/理解,可以毫不费力地扩展(并且没有样板if条款 - 通过添加更多标签),当然也不会留下任何运行时间.

它的调用语法非常友好,但当然可以包含在vanilla调用中:

void MyFunctionAlone() { MyFunction(Tag_DisableBonus{}); }
void MyFunctionBonus() { MyFunction(Tag_EnableBonus{}); }
Run Code Online (Sandbox Code Playgroud)

标签调度是一种广泛使用的通用编程技术,这里有一篇关于基础知识的好文章.