Как предотвратить компиляцию переданной лямбды, если аргументы не являются ссылками - PullRequest
3 голосов
/ 07 июня 2019

В одном из моих проектов я использую небольшую служебную функцию, которая принимает структуру Message и лямбда-функцию, которая изменяет эту структуру сообщения.

Теперь я непреднамеренно передала лямбду безнеобходимая ссылка &.Он прекрасно компилируется, но не дает желаемого результата.

Что касается меня, должно быть одно из двух следующих поведений:

  1. Забыть написать auto&, нопросто auto должно приводить к ошибкам компиляции
  2. Запись auto должна интерпретироваться как auto&.

Возможно предотвратить компиляцию в случае отсутствия & или даже лучше интерпретировать auto как auto& автоматически?

#include <iostream>
#include <functional>
#include <boost/variant.hpp>

struct Message {
    int x;
    int y;
};

void changeMessage(Message& m, const std::function<void(Message&)>& messageModifier) {
    std::cout << "Message before:" << m.x << " " << m.y << "\n";
    messageModifier(m);
    std::cout << "Message after:" << m.x << " " << m.y << "\n";
}

int main(int, char**) {
    {
        std::function<void(int&)> f = [](int&) {};
        std::function<void(int)> g = [](int) {};
        f = g; // This compiles. 
    }

    {
        std::function<void(int&)> f = [](int&) {};
        std::function<void(int)> g = [](int) {};
        //g = f; // This does not compile. Makes perfect sense.
    }

    Message m{ 10,20 };
    {
        changeMessage(m, [](auto m) { m.x++; m.y--; }); // User unintentionally forgot &! Can I prevent this from compilation?
        std::cout << "Message outside: " << m.x << " " << m.y << "\n";
    }
    {
        changeMessage(m, [](auto& m) { m.x++; m.y--; });
        std::cout << "Message outside: " << m.x << " " << m.y << "\n";
    }
}

1 Ответ

5 голосов
/ 07 июня 2019

Одним из способов предотвращения передачи Message по значению (а само auto никогда не является ссылкой) является отключение создания копии:

struct Message {
    Message() = default;
    Message(const Message&) = delete;

    int x;
    int y;
};

Другое решение, предложенное @ LF проверить, что лямбда не принимает значения:

template<class Fn>
void change_message(Message& m, Fn fn) {
    static_assert(!std::is_invocable_v<Fn, Message&&>);     
    fn(m);
}
...