Стандарт был изменен, так как вопрос (и большинство ответов) был опубликован в разрешении этого отчета о дефектах .
Способ создания работы for(:)
на вашем типе X
теперь является одним из двух способов:
X::begin()
и X::end()
, которые возвращают что-то, что действует как итератор begin(X&)
и end(X&)
, которые возвращают то, что действует как итератор, в том же пространстве имен, что и ваш тип X
.¹ И аналогично для const
вариантов. Это будет работать как на компиляторах, реализующих изменения отчета об ошибках, так и на компиляторах, которые этого не делают.
Возвращаемые объекты не обязательно должны быть итераторами. Контур for(:)
, в отличие от большинства частей стандарта C ++, указан в для расширения до эквивалента :
for( range_declaration : range_expression )
становится:
{
auto && __range = range_expression ;
for (auto __begin = begin_expr,
__end = end_expr;
__begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
, где переменные, начинающиеся с __
, предназначены только для изложения, а begin_expr
и end_expr
- это магия, которая вызывает begin
/ end
.²
Требования к началу / end return value: вы должны перегрузить pre- ++
, убедитесь, что выражения инициализации верны, binary !=
, которые могут использоваться в булевом контексте, унарные *
, которые возвращают что-то, что вы можете назначить-initialize range_declaration
с, и выставлять публичный деструктор.
Выполнение этого способом, который несовместим с итератором, вероятно, является плохой идеей, так как будущие итерации C ++ могут быть относительно бесцеремонными по поводу нарушения вашего кода, если вы это сделаете .
В стороне, вполне вероятно, что будущий пересмотр стандарта позволит end_expr
вернуть другой тип, чем begin_expr
. Это полезно в том смысле, что он позволяет оценивать «ленивый конец» (например, обнаружение нулевого окончания), который легко оптимизировать, чтобы быть таким же эффективным, как рукописный цикл C, и другие подобные преимущества.
¹ Обратите внимание, что петли for(:)
сохраняют любое временное значение в переменной auto&&
и передают его вам как lvalue. Вы не можете определить, выполняете ли вы повторное использование временного (или другого значения rvalue); такая перегрузка не будет вызываться контуром for(:)
. См. [Stmt.ranged] 1.2-1.3 из n4527.
² Либо вызовите метод begin
/ end
, либо только поиск в режиме ADL свободной функции begin
/ end
, или для поддержки массива в стиле C. Обратите внимание, что std::begin
не вызывается, если range_expression
не возвращает объект типа в namespace std
или не зависит от него.
В c ++ 17 диапазон - для выражения было обновлено
{
auto && __range = range_expression ;
auto __begin = begin_expr;
auto __end = end_expr
for (;__begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
с типами __begin
и __end
. Разделились.
Это позволяет конечному итератору не быть тем же типом, что и начать. Тип конечного итератора может быть «дозорным», который поддерживает только !=
с началом типа итератора.
Практическим примером того, почему это полезно, является то, что ваш конечный итератор может читать «проверьте ваш char*
чтобы увидеть, указывает ли он на '0'
«когда ==
с char*
. Это позволяет выражению C ++ range-for генерировать оптимальный код при итерации над буфером char*
с нулевым завершением.
struct null_sentinal_t {
template{},int> =0
>
friend bool operator==(Rhs const& ptr, null_sentinal_t) {
return !*ptr;
}
template{},int> =0
>
friend bool operator!=(Rhs const& ptr, null_sentinal_t) {
return !(ptr==null_sentinal_t{});
}
template{},int> =0
>
friend bool operator==(null_sentinal_t, Lhs const& ptr) {
return !*ptr;
}
template{},int> =0
>
friend bool operator!=(null_sentinal_t, Lhs const& ptr) {
return !(null_sentinal_t{}==ptr);
}
friend bool operator==(null_sentinal_t, null_sentinal_t) {
return true;
}
friend bool operator!=(null_sentinal_t, null_sentinal_t) {
return false;
}
};
live example в компиляторе без полного C + +17 поддержка; for
цикл вручную расширен.
Вот альтернатива, которая будет просто использовать последнюю строку / число, разделенную пробелом, независимо от того, сколько их, (если их нет, будет использоваться полное имя файла) !
[110 ] И если вы хотите переместить только те из них, которые имеют хотя бы один пробел, вы можете включить это в For
скобки.
@Echo Off
For %%A In ("D:\Sourcedir\* *.pdf") Do Call :L "%%A"
Exit /B
:L
Set "F=%~n1"
Set "F=%F: ="&Set "F=%"
If Not Exist "%~dp1%F%\" MD "%~dp1%F%"
Move /Y %1 "%~dp1%F%"
Почему вы использовали delims=
? Это удалит разделитель и приведет целую строку к %%a
.
Попробуйте это:
@ECHO OFF
SETLOCAL
SET "sourcedir=D:\Sourcedir"
PUSHD %sourcedir%
FOR /f "tokens=1,2,3" %%a IN (
'dir /b /a-d "*.pdf"'
) DO (
ECHO MD %%~nc
ECHO MOVE "%%a %%b %%c" .\%%~nc\
)
POPD
GOTO :EOF
Если не установлено delims=
, будет использоваться пробел. Таким образом, %%c
будет 10.pdf
, ~n
- извлечь его n часть.
Это основано на вашем вопросе, который вы можете объединить %%a
%%b
%%c
вместе с spaces
, тогда все просто.
Если ваши имена файлов более сложные, то лучше использовать внутренний цикл for
.
- Какой другой вопрос уже дал отличное решение.
Вы можете запустить цикл 2 для получения полного имени в первом цикле, затем разделить имя во втором цикле, получить третий токен, создать каталог и затем скопировать фактическое имя файла из первого цикла.
Таким образом, вам не нужно снова пытаться соединить имя, я знаю, что это работает, но это некрасиво и не является предпочтительным:
@echo off
setlocal enabledelayedexpansion
set "sourcedir=D:\Sourcedir"
pushd %sourcedir%
for %%a in (*.pdf) do (
set "var=%%a"
for /f "tokens=3" %%i in ("!var!") do (
echo md "%%~ni"
echo move "%%~a" "%%~ni"
)
)
popd
goto EOF
Для получения дополнительной информации об этих командах см. каждый из cmd.exe
, т. е.
for /?
set /?
setlocal /?
, , set
и setlocal
содержит очень конкретную информацию о задержке расширения.