在中途范围内使变量无法使用/无法访问

时间:2016-08-02 09:19:23

标签: c++

我们说我有这段代码:

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(非编译时解决方案)只是性能阻碍。

8 个答案:

答案 0 :(得分:6)

使名称在块中途无法使用的最简单方法是将其声明为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
}

或者你可以把代码放在&#34; here&#34;在嵌套块中,您可以将i重新声明为某种类型的变量,这可能会提供更好的诊断。

答案 1 :(得分: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
    }
}

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

(感谢LyingOnTheSky建议使用匿名结构。)

答案 2 :(得分:1)

您可以将使用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仅在内部块中定义。

答案 3 :(得分:1)

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

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

然后你的DoRequiredAction方法不必修改循环计数器。

答案 4 :(得分:0)

不知道是否适用:

#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

答案 5 :(得分:0)

问题

我不会讨好你,但这不符合你的预期:

  • 预处理器有一个#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 ? :-) 
}

答案 6 :(得分:0)

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

package com.example.swing;

import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.time.LocalDateTime;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class ExitDelay {
    public static void main(String[] args){

        Runtime.getRuntime().addShutdownHook(new Thread(() -> System.err.println(LocalDateTime.now() + " SHUTDOWN") ));

        SwingUtilities.invokeLater(() -> {

            JFrame frame = new JFrame();
            frame.setSize(1280,720);
            frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            frame.addWindowListener(new WindowAdapter(){
                @Override
                public void windowClosed(WindowEvent e){
                    System.err.println(LocalDateTime.now() + " CLOSE");

                    // Do all required cleanup stuff.
                    // check that threads are done, close files, commit transactions etc.
                    // ...

                    System.exit(0);
                }
            });
            frame.setVisible(true);

        });
    }
}

答案 7 :(得分:0)

这是另一种方式 - 使用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迭代器,关于何时一个迭代器设置&#39;一直是一个哲学问题。是平等的#39;到另一个。当其中一个迭代器对相等(在最短序列结束时停止)或它们全部相等(要求所有序列长度相同)时应该是吗?

在这个实现中,我使用了前者 - 当覆盖最短序列时停止迭代。您可以通过修改iterators<>

的相等运算符来调整此值

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

#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