Принудительно использовать noexcept для std::function?

Этот код компилируется и запускается, выдавая int:

#include <functional>

void r( std::function<void() noexcept> f ) { f(); }

void foo() { throw 1; }

int main()
{
    r(foo);
}

Однако я бы хотел, чтобы компилятор отклонил строку r(foo);, потому что r должна быть передана только функция noexcept. Спецификатор noexcept, по-видимому, игнорируется. Есть ли способ добиться этого?

Изменить: этот вопрос отличается от Должно ли передаваться знание об отсутствии исключений при передаче указателя на функцию? потому что я прошу исправить ситуацию, особенно в случае std::function.


person M.M    schedule 24.07.2015    source источник
comment
Возможен обман? stackoverflow.com/questions/14003502/   -  person Jonathan Potter    schedule 24.07.2015
comment
@JonathanPotter, безусловно, связан, хотя я не думаю, что это отвечает на мой вопрос (есть ли способ заставить r(foo) быть отклоненным)   -  person M.M    schedule 24.07.2015
comment
Если noexcept не является частью типа функции, то я думаю, что нет.   -  person Jonathan Potter    schedule 24.07.2015
comment
У меня тоже есть эта проблема. У меня есть функция, которая будет выполняться асинхронно, и поставщик обработчика хочет указать, что это исключение не будет распространяться. Это лишний сайт?   -  person Werner Erasmus    schedule 28.07.2015
comment
Похоже, что даже с C++ 11 noexcept, спецификация исключений все еще не является надежной частью C++ :-(   -  person Serge Ballesta    schedule 28.07.2015
comment
Код в вопросе перестал компилироваться в clang 4.0 и gcc 7.0 (похоже, компилировался нормально в более ранних версиях).   -  person Zitrax    schedule 27.03.2017


Ответы (1)


Я тоже наткнулся на эту проблему. Мое решение состояло в том, чтобы использовать объект делегирования (делегирование std::function). Делегат имеет спецификацию без исключений. Его еще можно улучшить (добавить ход и т. д.).

Вот оно:

#include <functional>

template <class FuncT>
struct NoExceptDelegate;

template <class R, class ... Args >
struct NoExceptDelegate<R(Args...)>
{
    NoExceptDelegate(std::function<R(Args...)>&& callback)
      : callback_(move(callback))
    {
        if (!callback_)
        {
            throw std::invalid_argument( "NoExceptDelegate requires a valid callback");
        }
    }

    template <class...ArgsU>
    R operator()(ArgsU&&... args) noexcept
    {
        return callback_(std::forward<ArgsU>(args)...);
    }

  private:
      std::function<R(Args...)> callback_;
};

Обычно это используется в качестве контракта в асинхронном интерфейсе, чтобы указать, что предоставленный обработчик не должен вызывать, например:

struct Interface
{
    virtual void doSomethingAsynchronous(
        NoExceptDelegate<void(int)> onCompletionResult) = 0;
    //...etc
};

Поскольку клиент является провайдером обратного вызова, NoExceptDelegate — это обещание от провайдера, которое не подведет. Провайдер должен убедиться, что по крайней мере std::function предоставлено возможность вызова.

person Werner Erasmus    schedule 28.07.2015
comment
Однако это не совсем правильно, так как std::function может выдать bad_function_call для пустого std::functions. - person Puppy; 28.07.2015
comment
В этом случае его можно было бы поймать во время построения, а не во время вызова. - person Werner Erasmus; 28.07.2015
comment
Да, кажется мне достаточно разумным, чтобы предотвратить обнуление как инвариант. - person Puppy; 28.07.2015
comment
На каком компиляторе это работает? У меня не сработало использование g++ -std=c++11 с g++ версии 5.4.0. - person Kurt M; 14.02.2017
comment
@KurtM, на этом этапе я тестировал его на GCC 4.8 и компиляторе, поставляемом с Visual Studio, а также на последнем clang на момент его написания. - person Werner Erasmus; 15.02.2017
comment
Правильно ли предположить, что нет способа сделать это гарантией времени компиляции? т.е. обещание лучшее, что мы можем сделать? - person Zitrax; 27.03.2017
comment
@Zitrax, я думаю, stackoverflow.com/questions/39763401/ на ваш вопрос, который сводится к - да, обещание в настоящее время является лучшим. Кстати, спасибо за исправление кода... - person Werner Erasmus; 27.03.2017
comment
Метод operator() также должен быть помечен const. Я также думаю, ИМО, что должен быть конструктор по умолчанию, который заполняет callback_ пустой лямбдой, такой как [](){}, просто для простоты перехода от std::function. - person Chris Watts; 03.01.2018