You do need to return *this from the assignment operator, and I'm surprised your code even compiled with that error, but I don't think that's relevant to your question. I think the reason why the copy constructor is not called is because it needs an actual object to copy from. Notice how its input parameters are passed by reference. The fun function returns by value, which means s3 is set equal to that value instead of an object that has that value.
My follow up question then is what is called in the declaration of s3, because that doesn't fit requirements for the default or copy constructors or the assignment operator.
I think one possible answer to my question is that something like that shouldn't ever happen because if a class that has the copy constructor or assignment operator overloaded is returned in a function, it should be returned by reference instead of by value to prevent a straight value by value copy (a shallow copy) being returned.
So am I correct to say that returning an object (that has the copy constructor overloaded) by value doesn't call the copy constructor of that object and instead just makes a shallow copy? I've never heard of it calling the copy constructor for a return value but I would think that would make sense, like in these types of cases.
I also think you should keep in mind that the copy constructor and assignment operator are not needed. I understand that you may be just doing this for practice with them but it's good to know that they are only really supposed to be used if the class somehow manages dynamic memory. This class does not, so in this case a shallow copy would be the same as a copy constructor or assignment operator overloaded copy.
If a function returns a class type by value, and the return statement's expression is the name of a non-volatile object with automatic storage duration, which isn't the function parameter, or a catch clause parameter, and which has the same type (ignoring top-level cv-qualification) as the return type of the function, then copy/move is omitted. When that local object is constructed, it is constructed directly in the storage where the function's return value would otherwise be moved or copied to. This variant of copy elision is known as NRVO, "named return value optimization".
sample is a class type: check
fun() is specified to return it by value: check
return statement's entire expression is just the name of a local variable: check
the return type and the local variable's types are the same: check
so when fun() executes the line "sample t;", it is calling the default constructor for main()'s local variable s3, and when it executes "t=s;", it is calling operator= on that main's s3