Использование универсальных ссылок с нешаблонными типами?

Я создаю простой оператор конвейера для векторов, который пользователь будет использовать следующим образом (псевдокод):

auto x = std::vector{1,2,3} | push_back(10)
                            | push_back(100)
                            ;

Теперь я попытался определить оператор трубопровода следующим образом:

template <typename T>
constexpr inline auto& operator| (std::vector<T>&& vec, std::invocable<std::vector<T>> auto&& func);

Но теперь vec только для r-значений (поскольку тип самого vec не выводится)! Но я хочу, чтобы он также привязывался к l-значениям. Как мне это сделать?

Одно уродливое, но функциональное решение состояло бы в том, чтобы сделать еще одну перегрузку для моего оператора конвейера, но с std::vector<int>&. Однако это не масштабируется (2 ^ n перегрузок для n аргументов!), и я считаю, что это та самая причина, по которой были созданы универсальные ссылки.

Как я могу использовать универсальные ссылки в этом случае?


person SomeProgrammer    schedule 10.04.2021    source источник
comment
Неужели invocable<T>, а не invocable<vector<T>>? Предположительно, push_back(100) - это вызываемый объект, который принимает vector<int>, а не вызываемый объект, который принимает int?   -  person Barry    schedule 10.04.2021
comment
@Барри да ты прав   -  person SomeProgrammer    schedule 10.04.2021


Ответы (1)


Конечно, вы можете использовать ссылки для переадресации.

template <typename T> inline constexpr bool is_vector = false;
template <typename ...P> inline constexpr bool is_vector<std::vector<P...>> = true;

template <typename T> requires is_vector<std::remove_cvref_t<T>>
constexpr auto &operator|(T &&vec, std::invocable<T> auto &&func);

Кроме того, помните, что операторы обычно должны создаваться в том же пространстве имен, что и один из их параметров, чтобы ADL мог их найти. Но нельзя трогать namespace std...

Кроме того, мне не нравится идея перегрузки оператора, в котором ни один из параметров не является классом, который вы создали. Если ваши пользователи не должны явно соглашаться на использование этих операторов, выполняя using namespace ...;.

person HolyBlackCat    schedule 10.04.2021
comment
Это не решает мою проблему, так как я не думаю, что теперь вы можете перегрузить оператор для других типов... - person SomeProgrammer; 10.04.2021
comment
Да, ваш последний пункт совершенно верен. Я буду помещать их в пространство имен под названием piping. - person SomeProgrammer; 10.04.2021
comment
@SomeProgrammer Нет, это не мешает вам перегружать | для других типов. С++ 20 requires очень хорош в этом отношении, но даже старый SFINAE на основе enable_if_t будет работать. - person HolyBlackCat; 10.04.2021
comment
Из шаблона cppreference‹Incrementable T› void g(T) требует Decrementable‹T›; template‹Decrementable T› void g(T) требует Incrementable‹T›; // некорректный формат, диагностика не требуется - person SomeProgrammer; 10.04.2021
comment
Должен ли я вернуться к SFINAE, чтобы он заработал? - person SomeProgrammer; 10.04.2021
comment
@SomeProgrammer Что, нет. Цитата cppreference говорит о том, что вы не можете повторно объявить функцию с теми же ограничениями, но с другим написанием. Конечно, вы можете перегрузить функцию, если ограничения другие. - person HolyBlackCat; 10.04.2021
comment
Оооо ок. Я такой тупой! Спасибо за объяснение. - person SomeProgrammer; 10.04.2021