- cpp17[meta cpp]
範囲 for 文は C++11 で導入された が、
begin
と end
の型が同じでなければならなかった。
C++17 でこの制限が緩和された。
for ( for-range-declaration : for-range-initializer ) statement
- for-range-initializer[italic]
- for-range-declaration[italic]
- statement[italic]
は以下のように展開される:
{
auto && __range = for-range-initializer;
auto __begin = begin-expr;
auto __end = end-expr; // __begin と __end は異なる型でもよい
for ( ; __begin != __end; ++__begin ) {
for-range-declaration = *__begin;
statement
}
}
- for-range-initializer[italic]
- for-range-declaration[italic]
- statement[italic]
C++11 では以下のように展開されていた:
{
auto && __range = for-range-initializer;
for ( auto __begin = begin-expr, __end = end-expr; // __begin と __end は同じ型でなければならない
__begin != __end;
++__begin ) {
for-range-declaration = *__begin;
statement
}
}
- for-range-initializer[italic]
- for-range-declaration[italic]
- statement[italic]
#include <iostream>
#include <string>
// delimiter や終端に到達したかどうか判定する述語
template<char delimiter>
struct EndOfDelimitedString
{
bool operator()(std::string::iterator it)
{
return *it != delimiter && *it != '\0';
}
};
template<char delimiter>
struct DelimitedString
{
std::string str;
// DelimitedString::begin と DelimitedString::end の型は異なる
std::string::iterator begin() { return str.begin(); }
EndOfDelimitedString<delimiter> end() const { return EndOfDelimitedString<delimiter>(); }
};
template<char delimiter>
bool operator!=(std::string::iterator it, EndOfDelimitedString<delimiter> e)
{
return e(it);
}
int main()
{
std::string str{"ABCDE, abcde|12345"};
for (auto c : str)
std::cout << c;
std::cout << '\n';
for (auto c : DelimitedString<','>{str})
std::cout << c;
std::cout << '\n';
for (auto c : DelimitedString<'|'>{str})
std::cout << c;
std::cout << '\n';
}
ABCDE, abcde|12345
ABCDE
ABCDE, abcde
GCC 4.9.3 でコンパイルすると begin
と end
の型が異なるため不適格となる:
for.cpp: In function ‘int main()’:
for.cpp:38:41: error: inconsistent begin/end types in range-based ‘for’ statement: ‘std::basic_string<char>::iterator {aka __gnu_cxx::__normal_iterator<char*, std::basic_string<char> >}’ and ‘EndOfDelimitedString<','>’
for (auto c : DelimitedString<','>{str})
^
for.cpp:38:41: error: conversion from ‘EndOfDelimitedString<','>’ to non-scalar type ‘std::basic_string<char>::iterator {aka __gnu_cxx::__normal_iterator<char*, std::basic_string<char> >}’ requested
for.cpp:42:41: error: inconsistent begin/end types in range-based ‘for’ statement: ‘std::basic_string<char>::iterator {aka __gnu_cxx::__normal_iterator<char*, std::basic_string<char> >}’ and ‘EndOfDelimitedString<'|'>’
for (auto c : DelimitedString<'|'>{str})
^
for.cpp:42:41: error: conversion from ‘EndOfDelimitedString<'|'>’ to non-scalar type ‘std::basic_string<char>::iterator {aka __gnu_cxx::__normal_iterator<char*, std::basic_string<char> >}’ requested
Range TS (technical specification) では Sentinel
(番兵) 等の範囲の終端を表すコンセプトが提案されているが、
型が begin
イテレータと異なるため範囲 for 文で使用できなかった
(N4128R1 §3.3.5)。
end
イテレータはインクリメント、デクリメント、間接参照されることがなく、この制限には実用的な意味がないため、緩和された。
Boost.Foreach のようなマクロは避けるべきだとされた。