我想根据模板 bool 参数有条件地在函数中声明局部变量。因此,如果这是真的,它应该在那里,否则不应该在那里,因为我不希望该变量在堆栈上分配内存或调用其构造函数。也可以是基本型。
我无法在 constexpr if 块中声明它,因为我需要在用法之间保持持久性。
我可以声明变量并添加
[[maybe_unused]]
。那么,有没有编译器优化可以保证不为变量分配内存呢?
template <bool T> void foo()
{
[[maybe_unused]] SomeLargeClass x;
if constexpr(T)
{
/* ... do something with x */
}
/* ... do something without x */
if constexpr(T)
{
/* ... do something more with x */
}
}
我尝试将声明替换为
std::enable_if_t<T, SomeLargeClass> x;
但它不起作用,因为
T==false
案例无法提供类型。为什么这不是SFINAE?
我还有其他选择吗?
As-if 规则可能会丢弃未使用的
SomeLargeClass
,但如果该类进行分配,情况会更复杂。
一种简单的权衡是使用 std::conditional
并在需要时使用 SomeLargeClass
,在其他情况下使用一些虚拟小班;
struct Dummy
{
// To be compatible with possible constructor of SomeLargeClass
template <typename ...Ts> Dummy(Ts&&...) {}
};
template <bool B> void foo()
{
[[maybe_unused]] std::conditional_t<B, SomeLargeClass, Dummy> x;
if constexpr(B) {
// ... do something with x
}
// ... do something without x
if constexpr(B) {
// ... do something more with x
}
}
作为替代方案,您可以重写函数,使您的类仅位于 constexpr 块中:
template <bool B> void foo()
{
const auto do_something_without_x = [](){
// ... do something without x
};
if constexpr(B) {
SomeLargeClass x;
// ... do something with x
do_something_without_x();
// ... do something more with x
} else {
do_something_without_x();
}
}
是的,编译器可以优化未使用的变量,假设它可以证明构造和销毁没有可观察到的副作用。
不是SFINAE,因为
not a type x;
使整个功能失败。没有其他选择foo
,因此这是一个硬错误。
是的,你可以专攻
foo
:
.
struct SomeLargeClass {};
template <bool T> void foo();
template <> void foo<false>() {
//... do something without x
}
template <> void foo<true>() {
SomeLargeClass x;
//... do something with x
foo<false>();
//... do something more with x
}
您可以使用局部变量
x
,但给它一个特殊的类型:
#include <iostream>
using std::ostream;
template <bool T> struct MaybeLargeType;
template <> struct MaybeLargeType<true> { int bigone; };
template <> struct MaybeLargeType<false> {};
ostream& operator<<(ostream& s, const MaybeLargeType<true>& o) { return s << o.bigone; }
ostream& operator<<(ostream& s, const MaybeLargeType<false>& o) { return s << "nope"; }
template <bool T> void foo() {
MaybeLargeType<T> x;
if constexpr(T) {
x.bigone = 1;
}
// other stuff
if constexpr(T) {
x.bigone += 3;
}
std::cout << x;
}
int main()
{
foo<true>();
foo<false>();
return 0;
}
这会移动变量 x 内的 LargeType,变量 x 的大小取决于模板参数,因此
if constexpr
块中的代码稍微冗长一些。
只是专业化方法的变体:
template <bool B>
class C
{
public:
void step1() { };
void step2() { };
};
template <>
class C<true>
{
public:
void step1() { /* use the large data*/ };
void step2() { /* use the large data*/ };
private:
// large data
};
template <bool B>
void foo()
{
C<B> x;
x.step1();
// x-unaware code
x.step2();
}
哪一个更好看?只是纯粹的口味问题...
我会把寻找更好的名字留给你。
如果您的类有一个简单的构造函数,请不用担心 - 编译器不会在堆栈上分配未使用的对象。
如果你的类有一个可以完成一些工作的构造函数,并且你知道它是浪费的,你可能想跳过这项工作。编译器可能仍然注意到该对象未被使用,并跳过构造函数。在对代码进行任何更改之前检查此项(过早优化)!
但是如果构造函数有一些副作用(不推荐),你就必须帮助编译器。一种方法是使用
unique_ptr
:
template <bool T> void foo()
{
unique_ptr<SomeLargeClass> x;
if constexpr(T)
{
... allocate x
... do something with *x
}
... do something without x
if constexpr(T)
{
... do something more with *x
}
}