struct Song { 
  Lyrics   lyr_; 
  Rhythm   rhy_; 
  Image    img_; 
  Notes  notes_; 
  string filename_; 
}; 
Don't use global variables.  Instead add data to an isolated context-- a repository of information a code module might need, allowing few arguments and decoupled code. Show me your flowcharts and conceal your tables, and I shall continue to be mystified. Show me your tables, and I won't usually need your flowcharts; they'll be obvious.[ The Mythical Man Month]
Bad programmers worry about the code. Good programmers worry about data structures and their relationships.[Linus Torvalds: https://lwn.net/Articles/193245/]
void apply(const Context&);
 
A read-only context.  If your code can use this, it absolutely should.  Downstream users and coders can't accidentally change something with bugs or misunderstandings.struct DataHeader { // pod 
  uint id; 
  int  numTimes; 
  doub radius; 
}; 
struct Data { 
  DataHeader         header; 
  vector<Trombones*> trombones; 
  void copy(Data*dest) { 
      memcpy(&dest->header, &header, sizeof(DataHeader)); 
      dest->trombones.resize(trombones.size()); 
      for (sz_t i=0; i<trombones.size(); ++i) 
        dest->trombones[i] = trombones[i]; 
  } 
}; 
"Plain old data" can be handled in fast, useful ways that aren't generally ok.  Factoring out the pod sub-context lets you take advantage of that systematically.struct SongAppendix { 
  Metadata     metadata; 
  vector<Song*> similar; 
  vector<Link*>   links; 
}; 
struct Song { 
  Lyrics   lyr_; 
  Rhythm   rhy_; 
  Image    img_; 
  Notes  notes_; 
  string filename_; 
  SongAppendix apdx; 
}; 
Factoring out optional additions keeps the core idea clear. nedwaves.com 2017 [171108]