I am looking at Bowie Owens' cppcon 2019 talk -- the can be found slides here. I came accross this talk as I was interested in introducing expression templates for a in-house numerical matrix-vector library that I am using, to improve its performance as well as the semantics of its client code.
I am not an expert in C++ and the talk's source code is not provided, so that I used the slides' example code snippets and came up with this :
//is_array_v template <class T> struct is_array { static constexpr bool value = false;};template <class T>struct is_array<std::vector<T>> { static constexpr bool value = true;};template <class T>constexpr bool is_array_v = is_array<std::remove_cvref_t<T>>::value;struct expression {};template <class callable, class... operands>class expr : public expression{ // I used reference semantics as I wish to avoid copying // but pay attention not to use ref to a temp // after the lifetime of the temp ended ... // I use variadic template to tackle any arity operator at once // and this mitigates the need for CRTP std::tuple<operands const &...> args_; callable f_;public: expr(callable f, operands const&... args) : args_(args...), f_(f) {} auto operator[](size_t const i) const { auto const call_at_index = [this, i](operands const&... a) { return f_(subscript(a, i)...); }; return std::apply(call_at_index, args_); }};template <class T>constexpr bool is_array_or_expression = is_array_v<T> || std::is_base_of_v<expression, std::remove_cvref_t<T>>;template <class A, class B>constexpr bool is_binary_op_ok = is_array_or_expression<A> || is_array_or_expression<B>;//subscript()template <class operand>auto subscript(operand const& v, size_t const i) { if constexpr (is_array_or_expression<operand>) { return v[i]; } else { return v; }}template <class LHS, class RHS>auto operator+(LHS const & lhs, RHS const& rhs){ // I use a lambda so that everything is located in ONE place return expr{ [](auto const& l, auto const& r) { return l + r; // deduced return types }, lhs, rhs };}class tridiagonal{ std::vector<double> v_;public: tridiagonal(std::vector<double> v) : v_(std::move(v)) {} template <class src_type> tridiagonal& operator=(src_type const& src) { size_t const I = v_.size(); for (size_t i = 0; i < I; ++i) { v_[i] = src[i]; } return *this; // this line was missing in the slides and in the talk }};
Yet when I write client code :
tridiagonal a({ 1.0, 2.0, 3.0 });tridiagonal b({ 0.5, 0.25, -4.5 });tridiagonal c = a + b; // problematic line
at compilation the compiler tells me, on the problematic line, that :
Error C2440 'initializing': cannot convert from '+::<lambda_1>,LHS,RHS>' to 'tridiagonal'with[ LHS=tridiagonal, RHS=tridiagonal}