#include <iostream>
#include <typeinfo>

// Would go into operator_auto.hpp

namespace operator_auto {
template <class T> struct operator_auto_type {
  using type = T;
};

struct operator_auto_imp {
  template <class T> typename operator_auto_type<T>::type operator=(T &&t) {
    return std::forward<T>(t);
  }
};

namespace {
operator_auto_imp _auto;
}
}

// End operator_auto.hpp

// Example adapted from http://l...content-available-to-author-only...s.com/2011/02/21/inferring-too-much/ by Motti Lanzkron

#define _CRT_SECURE_NO_WARNINGS // don't warn about strcpy in VS10
#include <cstring>
#include <iostream>

namespace my {
class string {
  size_t len;
  char *str;

  string(char *buf, size_t buf_len) : len(buf_len), str(buf) {}

public:
  explicit string(const char *src) : len(strlen(src)), str(new char[len + 1]) {
    std::strcpy(str, src);
  }

  string(const string &other) : len(other.length()), str(new char[len + 1]) {
    other.copy_to(str);
  }

  string &operator=(const string &other) {
    if (this != &other) {
      char *tmp = new char[other.len];
      other.copy_to(tmp);
      delete[] str;
      str = tmp;
      len = other.len;
    }
    return *this;
  }

  ~string() { delete[] str; }

  size_t length() const { return len; }

private:
  void copy_to(char *buff) const { std::strcpy(buff, str); }

  friend struct helper; // because you can't befriend a template
  struct helper {
    static void copy(const string &str, char *buf) { str.copy_to(buf); }

    template <class T> static void copy(const T &t, char *buf) {
      t.copy_to(buf);
    }

    static string create(char *buf, size_t len) { return string(buf, len); }
  };

public:
  template <class T> class concat {
    const T &lhs;
    const string &rhs;
    bool valid;

  public:
    concat(const T &left, const string &right)
        : lhs(left), rhs(right), valid(true) {}

    ~concat() { valid = false; }

    size_t length() const { return lhs.length() + rhs.length(); }

    void copy_to(char *dest) const {
      if (!valid)
        throw "Here there be dragons^H^H^H undefined behaviour";

      helper::copy(lhs, dest);
      helper::copy(rhs, dest + lhs.length());
    }

    operator string() const {
      char *buf = new char[length() + 1];
      copy_to(buf);
      return helper::create(buf, length());
    }

    concat<concat<T> > operator+(const string &str) const {
      return concat<concat<T> >(*this, str);
    }
  };

public:
  concat<string> operator+(const string &str) const {
    return concat<string>(*this, str);
  }

  friend std::ostream &operator<<(std::ostream &os, const string &str) {
    return os << str.str;
  }
};
}

namespace operator_auto {
template <class T> struct operator_auto_type<my::string::concat<T> > {
  using type = my::string;
};
}

#include <vector>
#include <limits>
std::vector<bool> to_bits(unsigned int n) {
  const int bits = std::numeric_limits<unsigned int>::digits;
  std::vector<bool> ret(bits);
  for (int i = 0, mask = 1; i < bits; ++i, mask *= 2)
    ret[i] = (n & mask) != 0;
  return ret;
}

namespace operator_auto {
template <> struct operator_auto_type<std::vector<bool>::reference> {
  using type = bool;
};
}

void test_ref(bool &b) { std::cout << "ref\n"; }
void test_ref(const bool &b) { std::cout << "c-ref\n"; }
void test_ref(bool &&b) { std::cout << "rv-ref\n"; }

int main() {
  {
    using operator_auto::_auto;
    my::string a("hello"), b(" "), c("world"), d("!");

    auto s = _auto = a + b + c + d;
    auto a1 = _auto = a;
    std::cout << s << std::endl;
  }

  {
    using operator_auto::_auto;
    bool b = _auto = to_bits(42)[3];
    auto a = _auto = to_bits(42)[3];
    std::cout << std::boolalpha << b << std::endl;
    std::cout << std::boolalpha << a << std::endl;

    // This should be r-value ref
    test_ref(_auto = to_bits(42)[3]);

    // This should be ref
    test_ref(_auto = b);
  }
}
