Це можна зробити, але для цього потрібно зробити кілька кроків. Спочатку напишіть a, template class
що представляє діапазон суміжних значень. Потім перешліть template
версію, яка знає, наскільки велика array
вона дляImpl
версії, яка займає цей суміжний діапазон.
Нарешті, впровадити contig_range
версію. Зверніть увагу, що for( int& x: range )
працює для contig_range
, тому що я реалізував begin()
і, end()
а покажчики є ітераторами.
template<typename T>
struct contig_range {
T* _begin, _end;
contig_range( T* b, T* e ):_begin(b), _end(e) {}
T const* begin() const { return _begin; }
T const* end() const { return _end; }
T* begin() { return _begin; }
T* end() { return _end; }
contig_range( contig_range const& ) = default;
contig_range( contig_range && ) = default;
contig_range():_begin(nullptr), _end(nullptr) {}
// maybe block `operator=`? contig_range follows reference semantics
// and there really isn't a run time safe `operator=` for reference semantics on
// a range when the RHS is of unknown width...
// I guess I could make it follow pointer semantics and rebase? Dunno
// this being tricky, I am tempted to =delete operator=
template<typename T, std::size_t N>
contig_range( std::array<T, N>& arr ): _begin(&*std::begin(arr)), _end(&*std::end(arr)) {}
template<typename T, std::size_t N>
contig_range( T(&arr)[N] ): _begin(&*std::begin(arr)), _end(&*std::end(arr)) {}
template<typename T, typename A>
contig_range( std::vector<T, A>& arr ): _begin(&*std::begin(arr)), _end(&*std::end(arr)) {}
};
void mulArrayImpl( contig_range<int> arr, const int multiplier );
template<std::size_t N>
void mulArray( std::array<int, N>& arr, const int multiplier ) {
mulArrayImpl( contig_range<int>(arr), multiplier );
}
(не перевірено, але дизайн повинен працювати).
Потім у вашому .cpp
файлі:
void mulArrayImpl(contig_range<int> rng, const int multiplier) {
for(auto& e : rng) {
e *= multiplier;
}
}
Це має мінус, що код, який циклічно переглядає вміст масиву, не знає (під час компіляції), наскільки великий масив, що може коштувати оптимізації. Ця перевага полягає в тому, що реалізація не повинна бути в заголовку.
Будьте обережні при явному побудуванні a contig_range
, оскільки, якщо ви його передасте, a set
він буде вважати, що set
дані є суміжними, що є хибним, і виконуватиме невизначену поведінку всюди. Єдиними двома std
контейнерами, над якими це гарантовано працювати, є vector
і array
(і масиви у стилі С, як це трапляється!). deque
незважаючи на те, що довільний доступ не є суміжним (небезпечно, він суміжний невеликими шматками!), list
навіть не близько, а асоціативні (упорядковані та невпорядковані) контейнери однаково несуміжні.
Отже, три конструктори, які я реалізував, де std::array
, std::vector
і масиви у стилі С, які в основному охоплюють основи.
Реалізація []
також проста, і між цим for()
і []
є більшість того, що вам потрібно array
, чи не так?
std::vector
.