Yeah, that works to suppress the warning. It’s still an unnecessary hoop to jump through though, and I’m not keen on unnecessary side effects, even if they are usually transparent. (F.ex., what if this is a tied scalar?)
It also loses the distinction between undef and an empty string, should that be needed.
Compared to the alternative length behaviour, it does remove the need for checking definedness of the return value if it’s needed in calculations, but you can simply apply your approach to the return value instead then: $len += 0; And that avoids the problem with side effects, as the return value of length is never overloaded.
I hadn’t considered this particular point, but as I said, any way the matter is turned, I cannot find a reason to prefer the currently implemented behaviour.
Library modules should not in general export built-in names like open or chdir as part of their default @EXPORT list, because these may sneak into someone else’s namespace and change the semantics unexpectedly. Instead, if the module adds that name to @EXPORT_OK, then it’s possible for a user to import the name explicitly, but not implicitly. That is, they could say
use Module 'open';
and it would import the open override. But if they said
they would get the default imports without overrides.
The foregoing mechanism for overriding built-in is restricted, quite deliberately, to the package that requests the import. There is a second method that is sometimes applicable when you wish to override a built-in everywhere, without regard to namespace boundaries. This is achieved by importing a sub into the special namespace CORE::GLOBAL::.