Matrix2x2 struct operators implementation in C++
Leading _

Some combinations of leading underscore are reserved specifically __ and _&lt;CapitalLetter&gt;, you're one character away from invalid code it's easier to stay away. See this answer.

Document Datatypes

The only way to discern what values are where in your matrix is to assume that the indices of your member variables are row/column, there is no documentation to that effect. I have to look at the out stream operator to figure that out. Always document assumptions or implicit behavior.

Scalar operations missing reverse operator

Matrix m1, m2;
double d;
m1 = m2 * d;


won't compile, you need to implement both version of asymmetric operators somehow.

Various matrix operations missing

For completeness there are quite a few operations missing, multiplication, inverse, determinant ... 

const qualifier on returns will cause extra copy

Matrix m1, m2, m3;
m1 = m2 + m3;


will create an extra copy of the result matrix on the assignment. You marked the return value of + as const this means that when it's assigned to a non-const variable a copy will be created. You are doing this on all the binary operators is there a specific reason you did this ? 

Fixed Matrices

There should be an easy way to get an identity, or a zero matrix it's not necessary but a nice to have. 

Operator + might be inefficient

While delegating the addition to the += operator saves you from repeating some code it could be better to construct the correct matrix in place and return it

const Matrix2x2 operator+(const Matrix2x2&amp; m1, const Matrix2x2&amp; m2)
{
    return Matrix2x2{m1._00 + m2._00, m1._01 + m2._01,
                     m1._10 + m2._10, m1._11 + m2._11};
}


Btw you could use this form for all you operations. 

Edit 1

const return value

You responded that this was something the lecturer advised you to do to protect against f() = x being written, if that is what he asks you should do it in that class. They are correct, having a const return value will prevent the compilation. This is a good thing as a statement like that most likely has no effect and the result of f() will immediately go out of scope. 

struct X;

X function() {};
const X constFunction() {};

X newValue;
// Compiles correctly but there is no effect (in most cases)
// as the result of function() will be discarded immediately
// There would be an effect if operator= is overridden and has 
// side effects. I would consider that a really bad idea.
function() = newValue; 

// The following line won't compile, therefor telling you
// "Don't do this"
constFunction() = newValue; 


In the code I usually work with i would definitely advise against it. In general I would be suspicious of any code that assigned directly to the result of a free function. I'd rather go with "Don't assign to the result of a free function" as a style rule. In modern c++ doing this prevents the use of move semantics on the result of your function. 

With member functions this would be different returning a reference or a const references is something that may happen a lot. So there are cases where you might actually write object.getter() = x. But it's dealing with references as results and not values.

Operator efficiency

I changed the wording to reflect this being an option, it's great that you went and checked the assembly, i did the same (https://godbolt.org/g/Kh3BsJ). For me it looks like that for clang and gcc the solutions are equivalent except for a different use of registers, I am not qualified to say how this affects the actual performance. For MSVC the case looks different the while in place solution is almost the same as what clang/gcc do, the two call path is definitely longer. As a general thought, the simpler your code is the easier it will be to optimize for the compiler. This is a discussion to be had when the need for optimization arises. You're doing great, keep going.