勝利宣言の後で申し訳ないけど
h()でハンドルするとしたら単に

  Z h(P const & p, Q const & q) {
    try {
      return f(p) * g(q)
    } catch (exception_invalid_arg_f e) {
      throw exception_invalid_arg("arg p is invalid");
    } catch (exception_invalid_arg_g e) {
      throw exception_invalid_arg("arg q is invalid");
    }
  }

みたいにリアス海岸になるわけでしょ
でもこれはpとqの異常の区別が下からあがってくる例外で区別できてしまう特殊例

  Z h2(P const & p, Q const & q0, Q const & q1) {
    return f(p) * g(q0) * g(q1);
  }

だとより複雑になる
じゃあこれ次の人やってみて