How to CreateDefaultSubobject with a blueprint derived class, without hardcoding an asset path?

For posterity: I didn’t want to give up on CreateDefaultSubobject, and decided I’d rather have 1 hardcoded asset reference than N hardcoded asset references, so this is what I did. It works but it’s really janky.

hvConfig.h

#pragma once

#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "Logging/LogMacros.h"
#include "hvConfig.generated.h"

HVCOMMON_API DECLARE_LOG_CATEGORY_EXTERN(HvConfig, Log, All);

UCLASS(BlueprintType, ClassGroup = (Vacui))
class HVCOMMON_API UhvConfig : public UDataAsset
{
	GENERATED_BODY()

private:
	static TObjectPtr<UhvConfig> CachedConfig;
	static UhvConfig* GetSingleton();

public:
	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	TMap<FSoftClassPath, FSoftClassPath> HvSubobjectClassOverrides;

	template<typename NativeUClass>
	TSoftClassPtr<NativeUClass> GetOverrideClassImpl() {
		static_assert(std::is_base_of<UObject, NativeUClass>::value, "NativeUClass must derive from UObject");
		UClass* nativeClass = NativeUClass::StaticClass();
		auto it = HvSubobjectClassOverrides.Find(nativeClass);
		if (!it) {
			UE_LOG(HvConfig, Display, TEXT("No override class registered for %s"), *nativeClass->GetName());
			return nativeClass;
		}
		UClass* overrideClass = it->TryLoadClass<NativeUClass>();
		if (!overrideClass) {
			UE_LOG(HvConfig, Fatal, TEXT("Failed to load override class %s"), *it->GetAssetPathString());
			return nativeClass;
		}
		UE_LOG(HvConfig, Display, TEXT("Found override class %s -> %s"), *nativeClass->GetName(), *it->GetAssetPathString());
		return overrideClass;
	}

	template<typename NativeUClass>
	static TSoftClassPtr<NativeUClass> GetOverrideClass() {
		return GetSingleton()->GetOverrideClassImpl<NativeUClass>();
	}
};

hvConfig.cpp

#include "hvConfig.h"

HVCOMMON_API DEFINE_LOG_CATEGORY(HvConfig);

// note: For non-Uobjects, you can also use TSharedPtr<>
TObjectPtr<UhvConfig> UhvConfig::CachedConfig = nullptr;

UhvConfig* UhvConfig::GetSingleton() {
	if (CachedConfig) {
		return CachedConfig.Get();
	}
	const TCHAR* configInstanceName = TEXT("hvConfig'/hvCore/hvConfig.hvConfig'");
	ConstructorHelpers::FObjectFinderOptional<UhvConfig> ConfigFinder(configInstanceName, LOAD_FindIfFail);
	UhvConfig* config = ConfigFinder.Get();
	if (!config) {
		UE_LOG(HvConfig, Error, TEXT("Failed to find config asset. Using a transitory one."));
		config = NewObject<UhvConfig>();
	}
	CachedConfig = config;
	return config;
}

myActor.cpp

AMyActor::AMyActor() {
	struct FConstructorStatics
	{
		TSoftClassPtr<UMyBaseComponent> DerivedClass;
		FConstructorStatics()
			: DerivedClass(UhvConfig::GetOverrideClass<UMyBaseComponent>())
		{}
	};
	static FConstructorStatics ConstructorStatics;

	UObject* subobj = CreateDefaultSubobject(FName("MyComponent"), UMyBaseComponent::StaticClass(), ConstructorStatics.DerivedClass.Get(), true, false);
	mMyComponent = static_cast<UMyBaseComponent*>(subobj);
}

Note: this code is inside a module named hvCommon, inside a plugin named hvCore. This is reflected in the HVCOMMON_API macro (which resolves to DLLIMPORT / DLLEXPORT), and the asset path for the singleton object hvConfig'/hvCore/hvConfig.hvConfig'