Это принципиальная проблема с тем, что происходит с недружелюбными вызовами SFINAE.Для получения дополнительной информации, проверьте P0826 .
Проблема в том, что когда вы вызываете это:
str.locked([](auto &s) { s = "Bar"; });
У нас есть две перегрузки locked
, и мы должны попробовать оба.Перегрузка non-const
работает нормально.Но const
one - даже если он все равно не будет выбран разрешением перегрузки - все равно нужно создать его экземпляр (это общая лямбда, поэтому, чтобы выяснить, что может быть decltype(std::forward<F>(f)(m_data))
, нужно создать его экземпляр) и этот экземплярне в теле лямбды.Тело находится вне непосредственного контекста, поэтому это не ошибка замещения - это серьезная ошибка.
Когда вы вызываете это:
str.locked([](std::string& s) { s = "Bar"; });
Нам не нужно смотреть наbody вообще во время всего процесса разрешения перегрузки - мы можем просто отклонить на сайте вызова (поскольку вы не можете передать const string
в string&
).
На самом деле не существует решения для этогопроблема в современном языке - вам нужно добавить ограничения на лямбду, чтобы сбой создания экземпляра происходил в непосредственном контексте замещения, а не в теле.Что-то вроде:
str.locked([](auto &s) -> void {
s = "Bar";
});
Обратите внимание, что нам не нужно делать этот SFINAE-дружественным - нам просто нужно убедиться, что мы можем определить тип возвращаемого значения без создания экземпляра тела.
Более полное языковое решение состояло бы в том, чтобы учесть "Вывод this
" (см. раздел в статье об этой конкретной проблеме).Но этого не будет в C ++ 20.