What I am talking about is the fact that *object++ behaves differently from *pointer++. ... So essentially, we get the opposite of how an ordinary pointer behaves.
That's not correct. They are exactly the same. The postfix++ is called first, then the *, for both.
The postfix++ returns the old value, before the increment, of course, but it's still called first in both cases.
Maybe you're getting confused because the * is applied to the old value, but it's not applied until after the increment (at least conceptually; there could be an optimization to do it first so a copy doesn't need to be made in the pure pointer case).
Here's an example with assembly, compiled without optimization:
Code:
#include <stdio.h>
int main() {
int a[] = {0,1,2,3,4};
int *p = a;
int n = *p++;
int m = *p++;
printf("%d %d\n", n, m);
return 0;
}
Code:
.LC0:
.string "%d %d\n"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $48, %rsp
movq %fs:40, %rax
movq %rax, -8(%rbp)
xorl %eax, %eax
movl $0, -32(%rbp)
movl $1, -28(%rbp)
movl $2, -24(%rbp)
movl $3, -20(%rbp)
movl $4, -16(%rbp)
; p = a
leaq -32(%rbp), %rax
movq %rax, -40(%rbp)
; p++
movq -40(%rbp), %rax
leaq 4(%rax), %rdx
movq %rdx, -40(%rbp) ; stores new value back to stack
; n = *p
movl (%rax), %eax ; uses old value, still in rax
movl %eax, -48(%rbp)
; p++
movq -40(%rbp), %rax
leaq 4(%rax), %rdx
movq %rdx, -40(%rbp)
; m = *p
movl (%rax), %eax
movl %eax, -44(%rbp)
; printf("%d %d\n", n, m)
movl -44(%rbp), %edx
movl -48(%rbp), %eax
movl %eax, %esi
leaq .LC0(%rip), %rdi
movl $0, %eax
call printf@PLT
I had a chance to look through you code. I feel like you're doing what I suggested but in a more complicated way. Instead of having the proxy, just define operator= in the push_back_iterant_t class. Define prefix++, postfix++, and * as no-ops. That's basically what the back_insert_iterator does.
I don't see the point of the nullptr part. When will the pointer ever be null?
BTW, all methods defined within a class declaration are automatically inline.
Code:
template <typename Container>
class fixed_iterant_t {
typedef typename Container::iterator iter_t;
typedef typename Container::value_type value_t;
iter_t current, end;
public:
fixed_iterant_t(Container& reference)
: current(reference.begin()), end(reference.end()) {}
fixed_iterant_t() : current(iter_t()), end(iter_t()) {}
fixed_iterant_t& operator++() {
if (current == end) throw;
++current;
return *this;
}
fixed_iterant_t operator++(int) {
auto tmp = *this;
++(*this);
return tmp;
}
value_t& operator*() {
if (current == end) throw;
return *current;
}
operator bool() { return current != end; }
};
template <typename Container>
fixed_iterant_t<Container> fixed_iterant(Container& reference) {
return fixed_iterant_t<Container>(reference);
}
template <typename Container>
class push_back_iterant_t {
Container* pointer;
public:
push_back_iterant_t(Container& reference) : pointer(&reference) {}
push_back_iterant_t& operator++() { return *this; }
push_back_iterant_t operator++(int) { return *this; }
push_back_iterant_t& operator*() { return *this; }
auto operator=(const typename Container::value_type& value) {
pointer->push_back(value);
return value;
}
};
template <typename Container>
push_back_iterant_t<Container> push_back_iterant(Container& reference) {
return push_back_iterant_t<Container>(reference);
}
#include <iostream>
#include <vector>
using namespace std;
int main() {
string buf;
auto pbi = push_back_iterant(buf);
string str = "Hello Iterant!";
auto fit = fixed_iterant(str);
while (fit) *pbi++ = *fit++;
*pbi = 0;
cout << "str: " << str << '\n';
cout << "buf: " << buf.data() << '\n';
}