Область переменных С++ внутри try

Рассмотрим этот код:

try {
    const Asdf &a = map1.at(index1);
    const Bsdf &b = map2.at(index2);
} catch(std::out_of_range&) {
    return false;
}
// <code>
std::cout<<a[b[42]]; // May throw std::out_of_range which should not be caught here.
return true;

<code> использует a и b. У меня есть два варианта:

  • Поместите <code> в блок try
  • Возьмите указатели в блоке try, затем разыщите их.

Первый вариант неверен, потому что если <code> throws std::out_of_range функция вернет false, это должно произойти только в случае сбоя поиска карты.

Второй вариант может быть немного уродливым:

const Asdf *a;
const Bsdf *b;
try {
    a = &map1.at(index1); // What?
    b = &map2.at(index2);
} catch(std::out_of_range&) {
    return false;
}
std::cout << (*a)[(*b)[42]];
return true;

Есть ли лучший способ? Что-то вроде try-except-else в Python было бы неплохо, но этого не существует в С++.

Ответ 1

Не нужно выполнять обработку исключений. std::map::find, учитывая ключ, даст вам итератор. Если элемент не существует в карте, то find вернет итератор end (т.е. map.end()).

При удалении ссылки на итератор вы получите пару значений. Первый - это ключ, а второй - объект.

auto aIt = map1.find(index1);
auto bIt = map2.find(index2);

if(aIt == map1.end() || bIt == map2.end())
{
    return false;
}

const Asdf &a = aIt->second;
const Bsdf &b = bIt->second;

std::cout << a[b[42]];

return true;

Обратите внимание, что итераторы в С++ определены так, что итератор begin находится в начале, а итератор end прошел последний элемент (http://en.cppreference.com/w/cpp/iterator/end), то есть диапазон для итераторов внутри контейнера: [начало, конец].

Ответ 2

Решение 1:

Зачем включать код в try catch, внедряя его в свой собственный блок catch catch, чтобы сделать разницу между этими двумя случаями?

try {
    const Asdf &a = map1.at(index1);
    const Bsdf &b = map2.at(index2);
    try {
        // <code>
        std::cout<<a[b[42]]; // May throw std::out_of_range which should not be caught here.
    } catch (std::out_of_range&) {}
} catch(std::out_of_range&) {
    return false;
}
return true;

Но, конечно же, при таком подходе вы не можете переслать внешнюю часть вашей функции out_of_range, которая произошла бы в вашем <code>.

Решение 2:

Другой альтернативой является просто проверить наличие ключей с помощью map::count() без необходимости обнаружения исключений:

if (map1.count(index1)==0 || map2.count(index2)==0) {
    return false; 
}
const Asdf &a = map1.at(index1);
const Bsdf &b = map2.at(index2);
// <code>
std::cout<<a[b[42]]; 
return true;

Ответ 3

Одна работа заключается в том, чтобы убедиться, что на самом деле карта содержит элемент. Это добавляет накладные расходы, но хуже, чем хуже, чем я знаю.

try{
    map1.at(index1);
    map2.at(index2);
}catch(std::out_of_range&){
    return false;
}
const Asdf &a=map1.at(index1);
const Bsdf &b=map2.at(index2);

Или, если написано немного лучше (извините, нет увеличения производительности, только читаемость), если вы не хотите пожертвовать ссылкой const.

if(map1.find(index1) == map1.end() || map2.find(index2) == map2.end()) return false;
const Asdf &a=map1.at(index1);
const Bsdf &b=map2.at(index2);

Вы также можете использовать std:: map:: const_iterator без блока try-catch.

std::map::const_iterator a = map1.find(index1);
if(a == map1.end()) return false;
std::map::const_iterator b = map1.find(index2);
if(b == map2.end()) return false;

Сделайте все, что доступно только для чтения a->second и b->second.

Ответ 4

Мне нравится решение Miguel, потому что он не включает обработку исключений (когда он не вызван).

Но помимо этого, здесь есть еще один вариант (который мне нравится быть коротким и удерживая число операций < <20 > ):

bool retval = false;

try{
    const Asdf &a=map1.at(index1);
    const Bsdf &b=map2.at(index2);
    retval = true;
    std::cout<<a[b[42]];
}catch(std::out_of_range&){
    return reval;
}

// more code?    

return reval;

Ответ 5

Нетрадиционным решением является использование захвата лямбда, чтобы расширить область ссылочных переменных за пределы области действия блока. Поскольку объекты, ссылающиеся на ссылки, действительны за пределами блока видимости, захваченные ссылки не являются устаревшими при использовании позже, пока объект карты остается в области видимости.

В качестве примера

#include <functional>
#include <vector>
int main()
{
    std::vector<std::vector< int > > map1 = { { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 },
                                                { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 },
                                                { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 },
                                                { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 },
                                                { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } };

    std::function<int()> fn;
    try{
        const auto &a = map1.at(1);
        const auto &b = map1.at(2);
        fn = [&]() {return a[b[1]]; };
    }
    catch (std::out_of_range&){
        return false;
    }
    fn(); // Any exception thrown here would be caught separately from the above try catch block
}