I tried doing the same thing with Clonable. Something like this seems to work (you’re right - it is not that obvious  , you have to get used to the CRTP pattern )
class IArchitecture 
{
   public:
	virtual torch::Tensor forward(torch::Tensor x) = 0;
	virtual void reset_parameters() = 0;
	virtual void reset() = 0;
	virtual std::shared_ptr<IArchitecture> clone() = 0;
};

typedef std::shared_ptr<IArchitecture> IArchitecturePtr;

template<class C>
struct IArchitectureClonable : public torch::nn::Cloneable<C>, IArchitecture
{
	virtual std::shared_ptr<IArchitecture> clone() override
	{
		return std::dynamic_pointer_cast<IArchitecture>(torch::nn::Cloneable<C>::clone());
	}
};

And you also have to implement reset method, which by looking at Linear implementation basically does the things that would usually be in the constructor (register_parameter, module …)
SimpleMLPImpl()
{
    this->reset();
}

virtual void reset() override
{
    fc1 = register_module("fc1", torch::nn::Linear(32, 64));
    fc2 = register_module("fc2", torch::nn::Linear(64, 1));
}