Honestly, I'd probably just cast them since it's the easiest way, and probably the most clear too. Presumably whatever code you have that sets up the links will ensure that only compatible types are ever linked together, so it shouldn't be dangerous.
A component architecture would probably work fine, but might be overkill. Presumably, it would amount to your code being something like this:
void SteamComponent::update_links() {
for (int i = 0; i < links.size(); i++) {
Link link = links[i];
TemperatureComponent *temperature = link->get_component('temperature');
if (temperature) {
temperature->update();
}
// Repeat as needed for each component it should care about
}
}
Bear in mind that I've never actually implemented a component architecture and my C++ is very rusty, but that's my understanding of how you do it. You'd need a base Component class, with subclasses for each type of component which would store the properties and methods that operate on it. A TemperatureComponent would store temperature and have functions for manipulating it. A PressureComponent might do the same for pressure, and so on. At this point you only need a single Link class with a list of components that are added to it when it's constructed, which tell you what it keeps track of.
Ultimately, that feels like it's just adding an extra layer of complexity and indirection without much benefit though. It would mostly be helpful if you were going to reuse those components between things, like if the ports, links and simulator components all needed a TemperatureComponent, for example. In video games, these kinds of component systems are usually used in many places, with a HealthComponent shared between the player, NPCs, destructible environments, and so on, for example.