In reply to chr_sue:
I added this line to my cfg_c class constructor:
void'(uvm_config_db#(string)::get(this, "*", "m_foo", this.m_foo));
That results in a compiler error:
formal and actual do not have assignment compatible data types (expecting datatype compatible with ‘class uvm_pkg::uvm_component’ but found ‘class my_pkg::cfg_c’ instead
When I change the first argument of get() to null, the get() works. This implies that there are differences in the handling of UVM field macros between UVM components and UVM objects. The behavior in my simulation suggests that UVM components walk their uvm_field_* space during creation, and query config_db for values using their hierarchical scope.
Since UVM objects are transient and do not have their own hierarchical scope, my guess is that their creation routine does not walk the uvm_field_* space. Rather, the user must call get() to assign those variables.