Как заставить функцию принимать только опорный параметр lvalue

Вот моя ситуация:

template<typename T, typename F>
inline
auto do_with(T&& rvalue, F&& f) {
    auto obj = std::make_unique<T>(std::forward<T>(rvalue));
    auto fut = f(*obj);
    return fut.then_wrapped([obj = std::move(obj)] (auto&& fut) {
        return std::move(fut);
    });
}

Я хочу, чтобы параметр шаблона F&& f принимал только ссылку non- const lvalue. Как я должен применять это?

Ответ 1

И я хочу, чтобы параметр шаблона F && f принимал только ссылку на константу lvalue.

Тогда вы не должны использовать ссылку пересылки. Вся идея пересылки - принять любую категорию значений и сохранить ее для будущих вызовов. Поэтому первое исправление заключается в том, чтобы не использовать здесь неправильную технику и вместо этого принимать ссылку на lvalue:

template<typename T, typename F>
inline
auto do_with(T&& rvalue, F& f) {
  // As before
}

Это должно заставить компилятор жаловаться, если вы попытаетесь передать значение r функции в функцию. Это не остановит компилятор от разрешения const lvalues, хотя (F будет выведено как const F1). Если вы действительно хотите предотвратить это, вы можете добавить еще одну перегрузку:

template<typename T, typename F>
inline
void do_with(T&& , F const& ) = delete;

Тип параметра F const& будет лучше соответствовать константам lvalues (и rvalues тоже, btw), поэтому этот будет выбран в разрешении перегрузки и сразу же вызовет ошибку, поскольку его определение будет удалено. Неконсольные lvalues будут перенаправлены на функцию, которую вы хотите определить.

Ответ 2

Вы можете взять f по ссылке lvalue и предотвратить неконстантные значения с static_assert и is_const:

template<typename T, typename F>
inline
auto do_with(T&& rvalue, F& f) {
    static_assert(!std::is_const<F>::value, "F cannot be const");
    …
}

С введением ограничений в С++ 20, вы будете иметь возможность использовать requires пункт вместо того, чтобы:

template<typename T, typename F>
inline
auto do_with(T&& rvalue, F& f) requires !std::is_const_v<F> {
    …
}

Ответ 3

добавить еще одно решение

template<typename T, typename F>
inline
auto do_with(T&& rvalue, F&& f) {
    static_assert(!std::is_const<typename std::remove_reference<F>::type>::value, "must be non-const");
    static_assert(std::is_lvalue_reference<F>::value, "must be lvalue reference");
    ...
}

или с SFINAE

template<typename T, typename F, typename std::enable_if<!std::is_const<typename std::remove_reference<F>::type>::value && std::is_lvalue_reference<F>::value, int>::type = 0>
inline
auto do_with(T&& rvalue, F&& f) {

}