For each cycle, make sure the support foot (one that's planted to the ground) moves back in linear speed. Use Set Keyframe Interpolation T > Linear for the support foot's first keyframe. You should also know the distance it moves back. Just Ctrl-drag (snap to increment) so the distance value is easy to reproduce, no need for guesswork.
Next, move the root bone, at the same distance, also in linear speed. Here's an example before moving the root bone, using David Rodriguez's Carmen rig (box moved linearly, standing in as the ground):

and after moving it [GIF] (box still as ground), and the animation's graph:

Support foot's position within the cycle represents how far the ground "moves". By moving it at linear speed, with known distance, it's easy to move the root bone in a way that will counter the support foot's.
NLA-based Walkcycle
The workflow is also applicable for non-linear animation, with slight difference. Create a separate action data for root bone's animation, put it above the walkcycle track:

Though the walkcycle's action is the same, the root bone's need to be extrapolated linearly. For the root bone's action, execute Channel > Extrapolation Mode Shift-E, and select Linear Extrapolation. That way, the root bone's animation strip only needs to be scaled to match the walkcycle's, no matter how long is repeated.
