Strong vs opt-in replay protection
There is an important distinction to be made between strong and opt-in replay protection. When a fork occurs, the former is always on and prevents any transaction on one side of the fork from being valid on the other side of the chain. Bitcoin Cash has strong replay protection for example.
Opt-in replay protection on the other hand is optional - you can create a transaction that won't be valid on the other side of the fork, but you are also able to have a transaction that is valid on both sides.
Strong replay protection is useful when you want to split from the main chain and remain an independent project. However, it can be detrimental when the changes you're proposing are meant to be an upgrade to the current code rather than forking off into a new project. This is why SegWit2x didn't opt to have a replay protection - it was meant to be an upgrade to the Bitcoin project and be the only version used. The only way to achieve that was to make it hard for both sides of the fork to coexist.
Problem with opt-in replay protection
While opt-in replay protection sounds like the proper way to go, there is one important problem to consider - how do you implement it in a way that will apply to all future forks?
In a perfect world, every fork would be carefully maintained and it would make sure to make its opt-in replay protection create transactions only valid on its own chain. However, as Bitcoin Gold and other projects have proven - we can't rely on forks being managed competently. Hence, we need a universal opt-in replay protection, one that is agnostic to any future forks (even those that don't honour any replay protection whatsoever) and creates transactions that will be only valid on one chain.
Universal opt-in replay protection
A fork that splits off from the main project can be caused by any alteration to the protocol. There is no universal way to differentiate between both sides of the fork ahead of time save for one - the blockchain history. You can mimic anything about the code, even pretend to be some different client, but for certain at some points the blockchains will diverge - otherwise we wouldn't be dealing with a fork. Once that is done, the data can be used to implement the replay protection.
If one could flag a transaction to only be valid if a given block hash is present in the blockchain, that would be enough to ensure it will always be possible to safely move coins around on any fork that might occur in the future.
If both sides of the fork honour that flag, only one side will include the transaction in the block. Do this on both sides and the coins will be safe to spend on two sides of the fork.
If only one side of the fork honours the flag however, the replay protection could still work, albeit with some limitations. You would need to create a transaction with the flag on the chain that doesn't honour it. This way the chain will include the transaction in its blockchain, but the main chain will reject it, since it would not recognise the block hash. After the first transaction is confirmed, it would be safe to spend the coins on the other side of the fork.
It is possible to implement a universal opt-in replay protection that will still be effective even if only one side of a given fork will respect its rules. This should be sufficient to protect one's bitcoins in an event of a possible future bitcoin fork.
The proposed implementation is rather simple and elegant. I came up with this idea when contemplating SegWit2x awhile back, but then became pleasantly surprised when I found out someone else already proposed it as a BIP115 a year before :). It's not part of the main codebase yet, but maybe by the time next fork rolls around we'll have something to protect our bitcoins with...