3

我希望能够避免在编译时知道条件为假时调用函数。现在我使用这样的东西:

template<bool Enabled>
void fun(params)
{
    //do nothing
}

template<>
void fun<true>(params)
{
    //do something with params.
}

我不喜欢这种方法的一点是,params即使函数体为空,也会对其进行评估。

我想要一个解决方案,当根本不调用函数并且当条件为假时不会评估参数(这可能在第一种情况下使用空函数进行优化,但我不能假设这对每个都是真的编译器)。

这甚至可能吗?

4

1 回答 1

13

是的。

template<bool b, typename Func, typename FuncElse>
struct compile_time_if_functor {
  void operator()( Func&& f, FuncElse&& ) const{
    std::forward<Func>(f)();
  }
};
template<typename Func, typename FuncElse>
struct compile_time_if_functor<false, Func, FuncElse> {
  void operator()( Func&&, FuncElse&& else_f  ) const{
    std:forward<FuncElse>(else_f)();
  }
};
template<bool b, typename Func, typename Else>
void compile_time_if( Func&& f, Else&& elsef ) {
  compile_time_if_functor<b, Func, Else> functor;
  functor(
    std::forward<Func>(f),
    std::forward<Else>(elsef)
  );
}
template<bool b, typename Func>
void compile_time_if( Func&& f ) {
  auto do_nothing = []{};
  compile_time_if<b>( std::forward<Func>(f), std::move(do_nothing) );
}

利用:

int main() {
  compile_time_if<expression>([&]{
    // code that runs iff expression is true
  });
  compile_time_if<expression2>([&]{
    // code that runs iff expression2 is true
  },[&]{
    // else clause, runs iff expression2 is false
  });
}

请注意{},如果在if. 因此,该代码需要在类型级别上格式正确且合法,但在运行时执行不必合法。没有在这些 lambda 中创建大小为 0 的数组!

一种更好的方法可以让你无限期地链接 if-else 块。如果我想这样做,我会使用命名运算符技巧。

首先,更改compile_time_if为 return std::integral_constant< bool, b >

然后,编写compile_time_else(Func&&)and compile_time_elseif<bool>(Func&&)which 返回类型,将其打包Func并覆盖operator*( std::true_type, X )operator*( std::false_type, X )运行或不运行Func并返回std::true_typeor std::false_type

最终目标将是这种语法:

If<expression>([&]{
  // block 1
})*Else([&]{
  // block 2
});

If<expression>([&]{
  // block 1
})*ElseIf<expression2>([&]{
  // block 2
})*ElseIf<expression3>([&]{
  // block 3
})*Else([&]{
  // block 4
});

允许全面的级联流量控制。你甚至可以这样做:

compile_time_switch<value>(
  Case<c0, FallThrough>(),
  Case<c1>([&]{
    // block 1
  }),
  Case<c2, Continue>([&]{
    // block 2
  }),
  Case<c3>([&]{
    // block 3
  }),
  Case<c4>([&]->CanContinue{
    // block 4
    if (condition) {
      return Continue;
    } else {
      return Break;
    }
  }),
  Case<c4>([&]{
  }),
  Default([&]{
  })
};

但这已经超越了我们自己。

有关如何以允许编译器在编译时操作流控制的方式来处理复制 C++ 语法的想法,请查看 boost phoenix。我在这里只是为了完整起见:实际上写那种东西并不是那么实用,因为一些可怜的草皮将不得不维护它,而最初几次你写这些东西你会做一份糟糕的工作!

于 2013-04-27T20:52:39.557 回答