Как передать rvalue std::vector в функцию, принимающую std::span

Допустим, у меня есть функция, принимающая std::span<int>, и я хочу вызвать ее со значением std::vector<int>:

void foo(std::span<int> s);

std::vector<int> make_vector();

int main() {  
  foo(make_vector());
}

Это даст мне ошибку:

Функция-кандидат нежизнеспособна: неизвестно преобразование из std::vector<int> в std::span<int> для 1-го аргумента.

Действительно, как правило, мы не хотим преобразовывать вектор, срок действия которого скоро истечет, в диапазон, который станет висячим в тот момент, когда вектор исчезнет. Но здесь мы явно не делаем ничего противозаконного.

Конечно, я могу сохранить вектор во временной переменной, и это сработает:

  auto v = make_vector();
  foo(v);

Но это более многословно и больше не является однострочным, что неудобно в некоторых контекстах.

Есть ли способ сообщить компилятору, что в данном конкретном случае можно выполнить преобразование?

Этот вопрос похож на: Как можно преобразовать std::vector в std::span?. Если вы считаете, что это другое, отредактируйте вопрос, поясните, чем он отличается и/или как ответы на этот вопрос не помогают решить вашу проблему.

Useless 30.08.2024 17:54

21.4k авторитетный пользователь теперь должен знать, как сделать минимальный воспроизводимый пример и задавая вопрос об ошибке, вызванной компилятором, код должен быть точным, чтобы воспроизвести ошибку, в противном случае другой пользователь может устранить ошибку, исправляя при этом другие ошибки компилятора. Код в вопросе вызывает другие ошибки, например error: use of undeclared identifier 'std'.

3CxEZiVlQ 30.08.2024 18:14

Вот MCVE.

Marek R 30.08.2024 18:25
void foo(std::span<const int>) работает с векторами значений rvalue; но убедитесь, что возвращаемое значение не зависит от ввода по ссылке или указателю, потому что оно будет болтаться и создавать UB.
Red.Wave 30.08.2024 18:25
godbolt.org/z/Mc18M44v5
Marek R 30.08.2024 18:28

@3CxEZiVlQ Вопрос в его нынешнем виде очень ясен, и на него легко ответить. Как пользователь 35k, вы могли бы отредактировать недостающие #include в вопросе, поскольку вас это очень волнует, но вместо этого по какой-то причине вы решили поступить как придурок. Думали ли вы вместо этого о том, чтобы быть полезным?

Barry 30.08.2024 18:43

@Useless Этот вопрос: «Почему построение span из vector работает?» Этот вопрос звучит так: «Почему построение span из rvalue vector не работает?» Совсем не тот вопрос.

Barry 30.08.2024 18:46

@Barry Тот же ответ: пожимаю плечами:

Useless 30.08.2024 22:38
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
8
114
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Если вы не планировали изменять содержимое span изнутри функции (что обычно верно), вы можете выразить свое намерение, явно указав span::element_type как const:

void foo(std::span<const int> s);

В этом случае можно построить span<const int> из rvalue vector<int>, даже если это не заимствованный_диапазон. См. перегрузку (7) конструктора std::span:

  1. Создает диапазон, представляющий собой представление диапазона range; ...
  • Эта перегрузка участвует в разрешении перегрузки только в том случае, если
    • ...
    • либо R соответствует заимствованному_диапазону, либо std::is_const_v<element_type> соответствует true

Вы можете написать auto& as_lvalue(auto&&):

template<class T>
T& as_lvalue(T&& t){return t;}

(Начиная с c++23 вы должны:

template<class T>
T& as_lvalue(T&& t){
  return static_cast<T&>(t);
}

по мере изменения правил возврата; делать это до 23 безвредно.)

затем оберните в него временный вектор:

foo(as_lvalue(make_vector()));

Это прояснит ваше намерение и устранит ошибку. Это также безопасно (поскольку буфер живет достаточно долго, чтобы завершить вызов функции), когда используется для приведения аргумента функции таким образом.

Если функция сохраняет (логические или нет) ссылки или возвращает их аргументам, отличным от lvalue, это может быть небезопасно.

работает на C++20, но не работает на C++23: error: cannot bind non-const lvalue reference of type 'std::vector<int>&' to an rvalue of type 'std::vector<int>' T& as_lvalue(T&& t){return t;}godbolt.org/z/zYsahjKPE

Gene 30.08.2024 23:31

@Gene В 23 нужно явно указать t в возврате.

Barry 31.08.2024 03:37

@Genogene Добавлен код, совместимый с C++23.

Yakk - Adam Nevraumont 01.09.2024 03:34

Другие вопросы по теме