【问题标题】:Make a variable unusable/unaccessible in scope halfway使变量在范围内中途不可用/不可访问
【发布时间】:2016-08-02 09:19:23
【问题描述】:

假设我有这个代码:

for (int i = 0; i < x.size(); i++) {
    auto &in = input[i];
    auto &func = functions[i];
    auto &out = output[i];
    // pseudo-code from here:
    unaccessiable(i);
    i = func(in); // error, i is not declared
    out = func(i); // error, i is not declared
    // useful when you mistake in/out for i
}

我需要在代码中的某一行之后实现变量不可访问或不可使用的效果。 (在这段代码中,在unaccessiable(i) 之后) 具体来说,我想禁用 for 循环的迭代器。

注意:这只是为了代码的正确性,除此之外没有任何意义。所以 lambdas(非编译时解决方案)只会阻碍性能。

【问题讨论】:

  • 我不完全确定,但如果您正在寻找释放变量使用的内存,我认为 this 会有所帮助。
  • 如果你不想使用迭代器,你的循环有什么意义?如果你想退出,休息一下怎么样?
  • 简单解决方案:将下面的所有内容 (unaccessible(i)) 包装到一个函数中(无论是否内联),并且不要将 i 传递给该函数。但我真的不明白这一点。
  • 这听起来像是“医生,我这样做时很痛”的情况之一。所以不要那样做——在这种情况下,更容易识别循环索引。为什么不将infuncout 传递给在循环内完成任何操作的函数?您正在绑定引用变量这一事实强烈表明您应该这样做。
  • 使用 lambda 很可能不会有任何开销。虽然 Alf 的解决方案是一个非常巧妙的技巧,但它确实不是您想要编写可维护代码的方式(我会更进一步:这是一个巨大的红色警告标志,应该被视为绝对的反模式)。重构你的代码,让变量生命周期反映代码结构,不要把代码结构弄得一团糟。

标签: c++


【解决方案1】:

使名称在块中途无法使用的最简单方法是将其声明为struct

for (int i = 0; i < x.size(); i++) {
    auto &in = input[i];
    auto &func = functions[i];
    auto &out = output[i];
    // pseudo-code from here:
    struct i;     // ←
    i = func(in); // error, i is a type
    // useful when you mistake out for i
}

或者,您可以将代码放在“这里”之后的嵌套块中,您可以在其中将i 重新声明为某种不良类型的变量,这可能会提供更好的诊断。

【讨论】:

  • 这是正确答案,其他答案建议的内部范围是错误的。
  • 请注意,这只适用于 C++,因为在 C 中,struct i 位于不同的“命名空间”(或任何 C 标准所称的)中。
  • 感觉这里的解决方案随着时间的推移而演变......我认为现在这是最好的解决方案。 (虽然还不是 100% 干净,但足够了)
  • @user694733 为什么?您只是在声明一个不完整的类型,它不应该在其他任何地方找到它(?)
  • @LyingOnTheSky 你不能用i直接引用C中的结构类型:你必须使用struct i,除非有typedef
【解决方案2】:

想到的一种方法是:

for (int i = 0; i < x.size(); i++) {
    auto &in = input[i];
    auto &func = functions[i];
    auto &out = output[i];
    {
        struct{} i;
        // ...
        i = func(in); // error
        // useful when you mistake out for i
    }
}

匿名类型与任何返回类型或参数类型都不兼容。 (可变参数函数除外)

(感谢 LyingOnTheSky 提出匿名结构的建议。)

【讨论】:

  • 只需将 nontype 转为匿名结构到 accpet(?)
  • 好吧,付钱给魔鬼的拥护者:如果我是坏人/bas... 编码器并在内部范围之后写i=25..... ;)
  • @LPs 是的,最佳答案更好(尽管我猜它是基于我的答案)。
【解决方案3】:

您可以将使用i 的部分放入自己的块中。

for (int counter = 0; counter < x.size(); counter++) {
    {
        auto i = counter;
        auto &in = input[i];
        auto &func = functions[i];
        auto &out = output[i];
    } // i goes out of scope here
    i = func(in); // error, i is not declared
    // useful when you mistake out for i
}

现在i 仅在内部块中定义。

【讨论】:

  • 只要移入/func/out 出内部作用域(给他们 ?\int 类型)。
  • 我的第一个,但 OP 想用循环 counter 变量来做到这一点。
  • 不确定这是否真的解决了 OP 的问题:如果他想确保没有人可以再访问循环变量,他需要使用您的解决方案使 counter 不可用
【解决方案4】:

尽管您想对循环应用的限制没有意义,但我会重写您的循环以确保没有人错误地使用计数器:

for(int i = 0;;) //whatever guard 
{
   DoRequiredAction(i);
}

那么您的DoRequiredAction 方法就不必修改循环计数器了。

【讨论】:

    【解决方案5】:

    不知道是否适用:

    #include <stdio.h>
    
    int main(void)
    {
        for (int i = 0, j; i < 20; i=++j)
        {
            j=i;
    
            printf("%d - ", i);
    
            i = 25;
        }
    
        printf("\n");
    
        return 0;
    }
    

    输出

    0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 
    

    正如您所看到的,无论在循环范围内使用 i 做什么,由于在循环开始时将 i 值备份到 j

    【讨论】:

      【解决方案6】:

      问题

      我不会取悦你,但这并不像你期望的那样可行:

      • 预处理器有一个#undef,但它只用于预处理器符号
      • 使用预处理器重新定义 i 也是不现实的,因为它不仅会影响 for 语句,还会影响其他所有内容(因此,搞砸任何未来对 i 的合法使用)
      • ODR 阻止在 for 语句的范围内重新定义变量
      • 作为唯一的解决方法,您可以按照 use31264 的回答中所述定义一个子集团,并希望没有人会在这个集团之外搞砸您的柜台。

      我从您的评论中了解您的隐藏要求:

      我不希望其他人错误地在循环中使用循环的迭代器。 它发生在我们身上太多次了。

      但无论采用何种方法,for 语句的设计方式都使得循环中总是有可能拧紧循环变量

      解决方案

      幸运的是,有一个解决方案可以防止这种情况发生。但是您必须围绕基于容器的习语重构您的代码。

      然后您可以选择range-for,这样任何人都没有机会干预您的迭代器:

      for (auto &x :  mycontainer)  {  // where mycontainer is a standard or a custom container
         const auto &in = x.input;     // ok, avoid they srcew up your input as well ;-)
         const auto &func = x.functions;
         auto &out = x.output;
         ...
         // haha : how can anyone screw the iterator now ? :-) 
      }
      

      【讨论】:

        【解决方案7】:

        如果您不希望变量可访问,为什么要创建它?

        #include <vector>
        #include <utility>
        
        template<class Item, class Container>
        std::size_t calc_offset(Item& item, Container& container)
        {
          return std::addressof(item) - std::addressof(*(container.begin()));
        }
        
        extern int input[];
        extern int output[];
        extern int (*functions[])(int);
        
        void test(const std::vector<int>& x)
        {
            for (auto& value : x) 
            {
              auto offset = [&x, &value] { return calc_offset(value, x); };
        
              auto &in = input[offset()];
              auto &func = functions[offset()];
              auto &out = output[offset()];
        
              out = func(in);
        
              // won't compile:
              // i = func(in);
            }
        }
        

        【讨论】:

        • 我认为将偏移量存储在局部变量中实际上使代码在这种情况下更具可读性。
        • @KonradRudolph 现在怎么样?
        【解决方案8】:

        这是另一种方式 - 不使用索引寻址,而是使用 zip 迭代器:

        这样,在管道之后,问题可能会这样表达:

        extern std::vector<int> input;
        extern std::vector<int> output;
        extern std::vector<int (*)(int)> functions;
        
        void test()
        {
            for (auto values : zip(input, functions, output))
            {
              auto &in = std::get<0>(values);
              auto &func = std::get<1>(values);
              auto &out = std::get<2>(values);
        
              out = func(in); // error, i is not declared
              // won't compile:
              // i = func(in);
            }
        }
        

        对于 zip 迭代器,总是存在一个哲学问题,即一个“迭代器集”何时与另一个“相等”。应该是当其中一个迭代器对相等时(在最短序列的末尾停止)还是当它们都相等时(要求所有序列的长度相同)?

        在这个实现中,我使用了前者——当最短序列被覆盖时停止迭代。您可以通过修改 iterators&lt;&gt; 的相等运算符来调整它。

        这是完整的示例,包括管道:

        #include <vector>
        #include <utility>
        #include <tuple>
        
        
        template<class...Iters>
        struct iterators
        {
          iterators(Iters... iters) : _iterators { iters... } {}
        
          bool operator==(const iterators& r) const {
            return !(_iterators != r._iterators);
          }
        
          bool operator!=(const iterators& r) const {
            return _iterators != r._iterators;
          }
        
          template<std::size_t...Is>
          auto refs(std::index_sequence<Is...>)
          {
            return std::tie(*std::get<Is>(_iterators)...);
          }
        
          auto operator*() {
            return refs(std::index_sequence_for<Iters...>());
          }
        
          template<std::size_t...Is>
          auto& plus(std::size_t n, std::index_sequence<Is...>)
          {
            using expand = int[];
            void(expand{0,
                        ((std::get<Is>(_iterators) += n),0)...
                       });
            return *this;
          }
        
          auto& operator+=(std::size_t n) {
            return plus(n, std::index_sequence_for<Iters...>());
          }
        
          auto& operator++() {
            return operator+=(1);
          }
        
          std::tuple<Iters...> _iterators;
        };
        
        template<class...Ranges>
        auto begins(Ranges&...ranges)
        {
          using iters_type = iterators<decltype(std::begin(ranges))...>;
          return iters_type(std::begin(ranges)...);
        }
        
        template<class...Ranges>
        auto ends(Ranges&...ranges)
        {
          using iters_type = iterators<decltype(std::begin(ranges))...>;
          return iters_type(std::end(ranges)...);
        }
        
        template<class...Ranges>
        struct ranges
        {
          ranges(Ranges&...rs)
            : _ranges(rs...)
            {}
        
          template<std::size_t...Is>
          auto make_begins(std::index_sequence<Is...>)
          {
            return begins(std::get<Is>(_ranges)...);
          }
        
          template<std::size_t...Is>
          auto make_ends(std::index_sequence<Is...>)
          {
            return ends(std::get<Is>(_ranges)...);
          }
        
          auto begin() {
            return make_begins(std::index_sequence_for<Ranges...>());
          }
        
          auto end() {
            return make_ends(std::index_sequence_for<Ranges...>());
          }
        
          std::tuple<Ranges&...> _ranges;
        };
        
        template<class...Ranges>
        auto zip(Ranges&...rs) {
          return ranges<Ranges...>(rs...);
        }
        
        extern std::vector<int> input;
        extern std::vector<int> output;
        extern std::vector<int (*)(int)> functions;
        
        void test()
        {
            for (auto values : zip(input, functions, output))
            {
              auto &in = std::get<0>(values);
              auto &func = std::get<1>(values);
              auto &out = std::get<2>(values);
        
              out = func(in); // error, i is not declared
              // won't compile:
              // i = func(in);
            }
        }
        

        哇,这么多代码……一定有一些处理开销……

        如果您启用优化(gcc 5.3 with -O2)则不会:

        test():
                pushq   %r15
                pushq   %r14
                pushq   %r13
                pushq   %r12
                pushq   %rbp
                pushq   %rbx
                subq    $8, %rsp
                movq    output+8(%rip), %r15
                movq    functions+8(%rip), %r14
                movq    input+8(%rip), %r13
                movq    input(%rip), %rbx
                movq    functions(%rip), %rbp
                movq    output(%rip), %r12
                jmp     .L4
        .L2:
                movl    (%rbx), %edi
                addq    $4, %r12
                addq    $4, %rbx
                call    *0(%rbp)
                addq    $8, %rbp
                movl    %eax, -4(%r12)
        .L4:
                cmpq    %rbx, %r13
                jne     .L2
                cmpq    %rbp, %r14
                jne     .L2
                cmpq    %r12, %r15
                jne     .L2
                addq    $8, %rsp
                popq    %rbx
                popq    %rbp
                popq    %r12
                popq    %r13
                popq    %r14
                popq    %r15
                ret
        

        【讨论】:

          猜你喜欢
          • 2014-05-28
          • 1970-01-01
          • 2021-06-23
          • 2021-06-17
          • 1970-01-01
          • 2016-04-29
          • 2013-05-07
          • 2015-12-10
          • 2017-05-21
          相关资源
          最近更新 更多