#include <type_traits>
#include <tuple>

namespace mplex {

template <std::size_t N> struct placeholder {
  static constexpr std::size_t value = N;

  template <typename T>
  friend auto constexpr eval(placeholder, T&& tuple) { return std::get<N>(std::forward<T>(tuple)); }
};
using p1 = placeholder<0>;
using p2 = placeholder<1>;
using p3 = placeholder<2>;

template <typename T> struct expression_template { using type = T; };

auto _1 = expression_template<p1>{};
auto _2 = expression_template<p2>{};
auto _3 = expression_template<p3>{};

template <int Value>
expression_template<std::integral_constant<int, Value> > int_();

template <typename T, T Value, typename X>
auto constexpr eval(std::integral_constant<T, Value>, X&&) { return Value; }

template <typename L, typename R>
struct mul {
  template <typename T>
  friend auto constexpr eval(mul, T&& tuple) { return eval(L{}, tuple)*eval(R{}, tuple); }
};
template <typename A, typename B>
auto operator*(expression_template<A>, expression_template<B>) -> expression_template<mul<A, B>>;

template <typename L, typename R>
struct add {
  template <typename T>
  friend auto constexpr eval(add, T&& tuple) { return eval(L{}, tuple)+eval(R{}, tuple); }
};
template <typename A, typename B>
auto operator+(expression_template<A>, expression_template<B>) -> expression_template<add<A, B>>;

template <typename L, typename R>
struct div {
  template <typename T>
  friend constexpr auto eval(div, T&& tuple) { return eval(L{}, tuple)/eval(R{}, tuple); }
};
template <typename A, typename B>
auto operator/(expression_template<A>, expression_template<B>) -> expression_template<div<A, B>>;

} // namespace mplex

int main()
{
  using namespace mplex;
  using newFunctor = decltype((_1 + int_<7>())*(_2/_3))::type;
  auto constexpr result = eval(newFunctor{}, std::make_tuple(1, 6, 3));
  static_assert(result == (1+7)*(6/3), "");
}
