Контроль с какими ссылками `T` связывает
Размышляя о том, что можно сделать, чтобы решить std::min
Одна из проблем, с которыми я сталкивался, это добавление перегрузки (фактически 3 - для каждой комбинации) для r-значений, которые будут удалены. Проблема в том, что T&&
будет ссылкой на пересылку, а не ссылкой на значение.
Я хочу отделить этот вопрос от std::min
конкретно и сделать его более общим. std::min
может быть взят в качестве примера, зачем вам такая вещь.
Позволяет упростить и обобщить проблему:
// this has the same problem as `std::min`: if t binds to a temporary,
// and the result is assigned to `auto&`, the result is a dangled reference
template <class T>
const T& foo(const T& t)
{
return t;
}
// incorrect attempt to prevent foo from being called with a temporary argument
// `T&&` is a forwarding reference, not an rvalue reference
template <class T>
const T& foo(T&& t) = delete;
Вопрос в том, как вы можете контролировать, к каким ссылкам относится общий параметр шаблона. T
может связать? И как это может масштабироваться для нескольких аргументов (как в std::min
дело)?
2 ответа
Учитывая ваш код, следующее не может скомпилировать
int i = 0;
foo(i); // deleted function
Причина, по которой выбрана перегрузка эталонной пересылки, заключается в том, что для соответствия другой требуется const
квалификация. Но если бы вы должны были написать
int const i = 0;
foo(i); // perfectly fine
В этом случае выбирается перегрузка, принимающая значение lvalue.
Таким образом, чтобы отклонить все значения, delete
д функция должна взятьT const&&
(Это то, чтоstd::ref
делает, чтобы отклонить значения)
template <class T>
const T& foo(const T& t)
{
return t;
}
template <class T>
const T& foo(T const&& t) = delete;
Вы можете сделать
template <typename T>
std::enable_if_t<std::is_rvalue_reference<T&&>::value>
foo(T&&) = delete;
Для 2 аргументов это становится:
template <typename T1, typename T2>
std::enable_if_t<
(std::is_rvalue_reference<T1&&>::value
|| std::is_rvalue_reference<T1&&>::value)
&& std::is_same<std::decay_t<T1>, std::decay_t<T2>>::value
>
foo(T1&&, T2&&) = delete;
Преторианская версия будет такой:
template <class T> void foo(const T&&, const T&) = delete;
template <class T> void foo(const T&, const T&&) = delete;
template <class T> void foo(const T&&, const T&&) = delete;