Допустим, у меня есть функция, принимающая 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);
Но это более многословно и больше не является однострочным, что неудобно в некоторых контекстах.
Есть ли способ сообщить компилятору, что в данном конкретном случае можно выполнить преобразование?
21.4k авторитетный пользователь теперь должен знать, как сделать минимальный воспроизводимый пример и задавая вопрос об ошибке, вызванной компилятором, код должен быть точным, чтобы воспроизвести ошибку, в противном случае другой пользователь может устранить ошибку, исправляя при этом другие ошибки компилятора. Код в вопросе вызывает другие ошибки, например error: use of undeclared identifier 'std'
.
Вот MCVE.
void foo(std::span<const int>)
работает с векторами значений rvalue; но убедитесь, что возвращаемое значение не зависит от ввода по ссылке или указателю, потому что оно будет болтаться и создавать UB.
@3CxEZiVlQ Вопрос в его нынешнем виде очень ясен, и на него легко ответить. Как пользователь 35k, вы могли бы отредактировать недостающие #include
в вопросе, поскольку вас это очень волнует, но вместо этого по какой-то причине вы решили поступить как придурок. Думали ли вы вместо этого о том, чтобы быть полезным?
@Useless Этот вопрос: «Почему построение span
из vector
работает?» Этот вопрос звучит так: «Почему построение span
из rvalue vector
не работает?» Совсем не тот вопрос.
@Barry Тот же ответ: пожимаю плечами:
Если вы не планировали изменять содержимое span
изнутри функции (что обычно верно), вы можете выразить свое намерение, явно указав span::element_type
как const:
void foo(std::span<const int> s);
В этом случае можно построить span<const int>
из rvalue vector<int>
, даже если это не заимствованный_диапазон. См. перегрузку (7) конструктора std::span:
- Создает диапазон, представляющий собой представление диапазона
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 В 23 нужно явно указать t
в возврате.
@Genogene Добавлен код, совместимый с C++23.
Этот вопрос похож на: Как можно преобразовать std::vector в std::span?. Если вы считаете, что это другое, отредактируйте вопрос, поясните, чем он отличается и/или как ответы на этот вопрос не помогают решить вашу проблему.