如何根据模板参数有条件地声明局部变量?

问题描述 投票:0回答:5

我想根据模板 bool 参数有条件地在函数中声明局部变量。因此,如果这是真的,它应该在那里,否则不应该在那里,因为我不希望该变量在堆栈上分配内存或调用其构造函数。也可以是基本型。

我无法在 constexpr if 块中声明它,因为我需要在用法之间保持持久性。

  1. 我可以声明变量并添加

    [[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 */
        }
    }
    
  2. 我尝试将声明替换为

    std::enable_if_t<T, SomeLargeClass> x;
    

    但它不起作用,因为

    T==false
    案例无法提供类型。为什么这不是SFINAE?

  3. 我还有其他选择吗?

c++ local-variables enable-if
5个回答
6
投票

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

4
投票
  1. 是的,编译器可以优化未使用的变量,假设它可以证明构造和销毁没有可观察到的副作用。

  2. 不是SFINAE,因为

    not a type x;
    使整个功能失败。没有其他选择
    foo
    ,因此这是一个硬错误。

  3. 是的,你可以专攻

    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
}

1
投票

您可以使用局部变量

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
块中的代码稍微冗长一些。


1
投票

只是专业化方法的变体

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

哪一个更好看?只是纯粹的口味问题...

我会把寻找更好的名字留给你。


0
投票

如果您的类有一个简单的构造函数,请不用担心 - 编译器不会在堆栈上分配未使用的对象。

如果你的类有一个可以完成一些工作的构造函数,并且你知道它是浪费的,你可能想跳过这项工作。编译器可能仍然注意到该对象未被使用,并跳过构造函数。在对代码进行任何更改之前检查此项(过早优化)!

但是如果构造函数有一些副作用(不推荐),你就必须帮助编译器。一种方法是使用

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
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.