c++ - несколько - оператор switch си




gcc при использовании оператора switch со случаем по умолчанию и лямбда-функцией (3)

Вам нужно приложить фигуру вашего тела case 'g' в фигурных скобках. Это происходит не из-за лямбда как таковой, а из-за создания любой новой переменной в случае.

Без дефолта я полагаю, что он не жалуется, потому что есть только одно место, которое может выполнить выполнение. Но с по умолчанию и без брекетов у вас есть проблема, потому что область действия f распространяется на код по default но там не будет инициализироваться.

Я не понимаю, почему этот код

#include <iostream>

class A {
    public:


    void foo(){
        char g = 'm';
        switch(g){
            case 'g':
                auto f = [](){std::printf("hello world\n");};
                f();
                break;
//            default:
//                std::printf("go to hell\n");
//                break;
        }
    };
};



int main(int iargc, char *iargv[]){
    A a;
    a.foo();
}

компилирует (и работает) отлично, тогда как при раскомментировании инструкции по умолчанию

#include <iostream>

class A {
    public:


    void foo(){
        char g = 'm';
        switch(g){
            case 'g':
                auto f = [](){std::printf("hello world\n");};
                f();
                break;
            default:
                std::printf("go to hell\n");
                break;
        }
    };
};



int main(int iargc, char *iargv[]){
    A a;
    a.foo();
}

дает мне следующее сообщение об ошибке

test.cpp:15:13: error: jump to case label [-fpermissive]
         default:
         ^
test.cpp:12:22: error:   crosses initialization of A::foo()::__lambda0 f
             auto f = [](){std::printf("hello world\n");};

Я могу использовать инструкцию по умолчанию, если я прокомментирую функцию лямбда.

Я использую gcc 4.8.5.


Сообщение об ошибке сообщает вам, в чем проблема. Переход к метке по default происходит от точки, где f не находится в области видимости до точки, где она находится в области видимости, пропуская ее инициализацию.

Соответствующим правилом стандарта является:

6.7 Декларация о заявлении [stmt.dcl]

Можно передать в блок, но не таким образом, чтобы обходить объявления с инициализацией. Программа, которая перескакивает с точки, где переменная с продолжительностью автоматического хранения не находится в области до точки, где она находится в области, плохо сформирована, если только переменная не имеет скалярного типа, типа класса с тривиальным конструктором по умолчанию и тривиальным деструктором, cv-квалификационная версия одного из этих типов или массив одного из предыдущих типов и объявляется без инициализатора (8.6).

Когда у вас есть только один case для коммутатора, нет возможности перепрыгнуть через инициализацию, поскольку единственное место, где вы можете ввести оператор switch, находится на первом ярлыке case, который не пропускает инициализацию. Если вы не обойдете инициализацию переменной, тогда проблем нет.

Вы не получаете ошибку для таких типов, как double или int потому что они являются скалярными типами (поэтому, если вы перепрыгиваете через их инициализацию, они находятся в области видимости, но не инициализированы). Тип замыкания, созданный лямбдой, не является скалярным типом и не объявляется без инициализатора.


switch(g){
  case 'g':
    auto f = [](){std::printf("hello world\n");};
    f();
    break;
  default:
    std::printf("go to hell\n");
    break;
}

switch передает управление одному из своих меток. Все эти метки находятся в одном блоке, введенном оператором switch.

В стандарте (N4296 §6.7 / 3) говорится (акцент мой):

Можно передать [управление] в блок, но не таким образом, чтобы обходить объявления с инициализацией . Программа, которая перескакивает с точки, где переменная с продолжительностью автоматического хранения не находится в области до точки, где она находится в области, плохо сформирована, если только переменная не имеет скалярного типа, типа класса с тривиальным конструктором по умолчанию и тривиальным деструктором, cv-квалификационная версия одного из этих типов или массив одного из предыдущих типов и объявляется без инициализатора.

Таким образом, поскольку можно передать управление непосредственно на метку по default , после объявления и инициализации лямбда f (что не является тривиально конструктивным), ваша программа плохо сформирована и по праву отвергается компилятором.





switch-statement