【问题标题】:How to conditionally declare a local variable based on a template argument?如何根据模板参数有条件地声明局部变量?
【发布时间】:2022-01-12 08:59:49
【问题描述】:

我想根据模板 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
}

}
  1. 我尝试将声明替换为
std::enable_if_t<T, SomeLargeClass> x;

但它不起作用,因为 T==false 案例无法提供类型。为什么这不是 SFINAE?

  1. 我还有其他选择吗?

【问题讨论】:

  • 你的编译器实际生成了什么?未使用的变量可能会被优化掉,除非它们是非平凡的类型。
  • 请记住,在 C++ 中,有 as-if 规则。您编写的代码只是对您想要完成的操作的描述——编译器生成的最终代码可能与源代码完全不同,只要程序产生正确的结果即可。这一切都归功于优化。声明未使用的变量——一个好的编译器会优化掉这些变量。
  • 作为最后的手段,您始终可以专门化 foo(并将不需要可选变量的任何内容重构为单独的函数以避免重复)
  • 持久性是什么意思?
  • @PasserBy 我的意思是我需要函数范围内的任何地方的变量。它不应局限于 if 子句之类的子范围。

标签: c++ local-variables enable-if


【解决方案1】:

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
    }
}

【讨论】:

  • 或者只是将 Dummy 专门用于 bool 以成为“大”类型的持有者。
  • @alagner:AdriaandeGroot 提出了类似的解决方案。不过命名似乎更复杂。
【解决方案2】:
  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
}

【讨论】:

  • 前提:我赞成,因为我认为这种方法比其他答案更清晰。我能问一下为什么我们应该使用这样的东西而不是使用旧的两个不同的函数(foo_true 和 foo_false)来避免模板吗?
  • @Federico 问题的前提是foo 是一个模板。如果您想在编译时在foo_truefoo_false 之间进行选择,那么最简单的方法是使用模板foo
  • 感谢 3 中给出的建议。由于在我给出的简化示例中它是有意义的,但在实际使用中不太理想。代码的流程是如此交织在一起。这是一些旧代码。
  • 第二项,实际上函数不会失败,因为我只在正确的 constexpr if 块中使用变量。但我现在明白了。我假设编译器不会检查它是否失败。一般来说,它可能会失败......非常感谢。这真的很有帮助。
  • 建议从foo&lt;true&gt;调用foo&lt;false&gt;以避免代码重复...
【解决方案3】:

你可以使用局部变量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;
}

这会将 LargeType 移动到变量 x 中,该变量的大小取决于模板参数,因此您在 if constexpr 块中的代码略显冗长。

【讨论】:

    【解决方案4】:

    只是specialisation approach 的一个变体:

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

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

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

    【讨论】:

      【解决方案5】:

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

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

      但是如果构造函数有一些副作用(不推荐),你必须帮助编译器。一种方法是使用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
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多