Allow ProductManifold default construction

In many cases, manifolds stored in ProductManifold have a default
constructor which can simplify ProductManifold initialization even
further. Allow default construction of ProductManifold in this case.

Change-Id: I29b2612870c02232556688019a77049709684a55
diff --git a/docs/source/nnls_modeling.rst b/docs/source/nnls_modeling.rst
index 5bc5382..51afcec 100644
--- a/docs/source/nnls_modeling.rst
+++ b/docs/source/nnls_modeling.rst
@@ -1528,7 +1528,7 @@
 In cases, where a parameter block is the Cartesian product of a number
 of manifolds and you have the manifold of the individual
 parameter blocks available, :class:`ProductManifold` can be used to
-construct a :class:`Manifold` of the cartesian product.
+construct a :class:`Manifold` of the Cartesian product.
 
 For the case of the rigid transformation, where say you have a
 parameter block of size 7, where the first four entries represent the
@@ -1537,8 +1537,16 @@
 
 .. code-block:: c++
 
-   ProductManifold<QuaternionManifold, EuclideanManifold<3>> se3{QuaternionManifold{}, EuclideanManifold<3>{}};
+   ProductManifold<QuaternionManifold, EuclideanManifold<3>> se3;
 
+Manifolds can be copied and moved to :class:`ProductManifold`:
+
+.. code-block:: c++
+
+   SubsetManifold manifold1(5, {2});
+   SubsetManifold manifold2(3, {0, 1});
+   ProductManifold<SubsetManifold, SubsetManifold> manifold(manifold1,
+                                                            manifold2);
 
 In C++17, the template parameters can be left out as they are automatically
 deduced making the initialization much simpler:
@@ -2153,7 +2161,7 @@
 In cases, where a parameter block is the Cartesian product of a number
 of manifolds and you have the local parameterization of the individual
 manifolds available, :class:`ProductParameterization` can be used to
-construct a local parameterization of the cartesian product. For the
+construct a local parameterization of the Cartesian product. For the
 case of the rigid transformation, where say you have a parameter block
 of size 7, where the first four entries represent the rotation as a
 quaternion, a local parameterization can be constructed as
diff --git a/examples/bundle_adjuster.cc b/examples/bundle_adjuster.cc
index d42a0d7..19c9c4b 100644
--- a/examples/bundle_adjuster.cc
+++ b/examples/bundle_adjuster.cc
@@ -306,8 +306,7 @@
   if (CERES_GET_FLAG(FLAGS_use_quaternions) &&
       CERES_GET_FLAG(FLAGS_use_manifolds)) {
     Manifold* camera_manifold =
-        new ProductManifold<QuaternionManifold, EuclideanManifold<6>>{
-            QuaternionManifold{}, EuclideanManifold<6>{}};
+        new ProductManifold<QuaternionManifold, EuclideanManifold<6>>{};
     for (int i = 0; i < bal_problem->num_cameras(); ++i) {
       problem->SetManifold(cameras + camera_block_size * i, camera_manifold);
     }
diff --git a/include/ceres/product_manifold.h b/include/ceres/product_manifold.h
index bd6131f..b7ebe4d 100644
--- a/include/ceres/product_manifold.h
+++ b/include/ceres/product_manifold.h
@@ -55,41 +55,40 @@
 //
 // Example usage:
 //
-// ProductManifold<QuaternionManifold, EuclideanManifold<3>>
-//      se3(QuaternionManifold(), EuclideanManifold<3>());
+// ProductManifold<QuaternionManifold, EuclideanManifold<3>> se3;
 //
 // is the manifold for a rigid transformation, where the rotation is
 // represented using a quaternion.
 //
+// Manifolds can be copied and moved to ProductManifold:
+//
+// SubsetManifold manifold1(5, {2});
+// SubsetManifold manifold2(3, {0, 1});
+// ProductManifold<SubsetManifold, SubsetManifold> manifold(manifold1,
+//                                                          manifold2);
+//
 // In C++17, the template parameters can be left out as they are automatically
 // deduced making the initialization much simpler:
 //
 // ProductManifold se3{QuaternionManifold{}, EuclideanManifold<3>{}};
 //
-// The manifold implementations must be either copyable or moveable to be usable
-// in a ProductManifold.
-template <typename... Manifolds>
+// The manifold implementations must be either default constructible, copyable
+// or moveable to be usable in a ProductManifold.
+template <typename Manifold0, typename Manifold1, typename... ManifoldN>
 class ProductManifold final : public Manifold {
-  struct InitTag {};
-
  public:
-  template <
-      typename Manifold0,
-      typename Manifold1,
-      typename... ManifoldN,
-      std::enable_if_t<std::is_constructible<std::tuple<Manifolds...>,
-                                             Manifold0,
-                                             Manifold1,
-                                             ManifoldN...>::value>* = nullptr>
-  ProductManifold(Manifold0&& manifold0,
-                  Manifold1&& manifold1,
-                  ManifoldN&&... manifolds)
-      : ProductManifold{
-            InitTag{},  // Use InitTag to disambiguate public/private
-                        // constructors, which both use variadic arguments
-            std::forward<Manifold0>(manifold0),
-            std::forward<Manifold1>(manifold1),
-            std::forward<ManifoldN>(manifolds)...} {}
+  // ProductManifold constructor perfect forwards arguments to store manifolds.
+  //
+  // Either use default construction or if you need to copy or move-construct a
+  // manifold instance, you need to pass an instance as an argument for all
+  // types given as class template parameters.
+  template <typename... Args,
+            std::enable_if_t<std::is_constructible<
+                std::tuple<Manifold0, Manifold1, ManifoldN...>,
+                Args...>::value>* = nullptr>
+  explicit ProductManifold(Args&&... manifolds)
+      : ProductManifold{std::make_index_sequence<kNumManifolds>{},
+                        std::forward<Args>(manifolds)...} {}
 
   int AmbientSize() const override { return ambient_size_; }
   int TangentSize() const override { return tangent_size_; }
@@ -127,27 +126,22 @@
   }
 
  private:
-  static constexpr std::size_t kNumManifolds = sizeof...(Manifolds);
+  static constexpr std::size_t kNumManifolds = 2 + sizeof...(ManifoldN);
 
-  // In the public constructor, we enforce at least two parameters. Then, the
-  // public constructor delegates the arguments to this private constructor to
-  // be able to construct-initialize the manifolds tuple. The delegation is
-  // necessary to avoid requiring the manifolds to be default constructible.
-  // InitTag is used as a token to disambiguate between both variadic parameter
-  // constructors.
-  template <typename... Args>
-  explicit ProductManifold(InitTag, Args&&... manifolds)
-      : buffer_size_{(std::max)(
-            {(manifolds.TangentSize() * manifolds.AmbientSize())...})},
-        ambient_sizes_{manifolds.AmbientSize()...},
-        tangent_sizes_{manifolds.TangentSize()...},
+  template <std::size_t... Indices, typename... Args>
+  explicit ProductManifold(std::index_sequence<Indices...>, Args&&... manifolds)
+      : manifolds_{std::forward<Args>(manifolds)...},
+        buffer_size_{
+            (std::max)({(std::get<Indices>(manifolds_).TangentSize() *
+                         std::get<Indices>(manifolds_).AmbientSize())...})},
+        ambient_sizes_{std::get<Indices>(manifolds_).AmbientSize()...},
+        tangent_sizes_{std::get<Indices>(manifolds_).TangentSize()...},
         ambient_offsets_{ExclusiveScan(ambient_sizes_)},
         tangent_offsets_{ExclusiveScan(tangent_sizes_)},
         ambient_size_{
             std::accumulate(ambient_sizes_.begin(), ambient_sizes_.end(), 0)},
         tangent_size_{
-            std::accumulate(tangent_sizes_.begin(), tangent_sizes_.end(), 0)},
-        manifolds_{std::forward<Args>(manifolds)...} {}
+            std::accumulate(tangent_sizes_.begin(), tangent_sizes_.end(), 0)} {}
 
   template <std::size_t Index0, std::size_t... Indices>
   bool PlusImpl(const double* x,
@@ -265,6 +259,7 @@
     return result;
   }
 
+  std::tuple<Manifold0, Manifold1, ManifoldN...> manifolds_;
   int buffer_size_;
   std::array<int, kNumManifolds> ambient_sizes_;
   std::array<int, kNumManifolds> tangent_sizes_;
@@ -272,7 +267,6 @@
   std::array<int, kNumManifolds> tangent_offsets_;
   int ambient_size_;
   int tangent_size_;
-  std::tuple<Manifolds...> manifolds_;
 };
 
 #ifdef CERES_HAS_CPP17
diff --git a/internal/ceres/manifold_test.cc b/internal/ceres/manifold_test.cc
index 60f98d2..6210c7a 100644
--- a/internal/ceres/manifold_test.cc
+++ b/internal/ceres/manifold_test.cc
@@ -428,6 +428,57 @@
   EXPECT_EQ(manifold1.TangentSize(), manifold2.TangentSize());
 }
 
+struct NonDefaultConstructibleManifold : ceres::Manifold {
+  NonDefaultConstructibleManifold(int, int) {}
+  int AmbientSize() const override { return 4; }
+  int TangentSize() const override { return 3; }
+
+  bool Plus(const double* x,
+            const double* delta,
+            double* x_plus_delta) const override {
+    return true;
+  }
+
+  bool PlusJacobian(const double* x, double* jacobian) const override {
+    return true;
+  }
+
+  bool RightMultiplyByPlusJacobian(const double* x,
+                                   const int num_rows,
+                                   const double* ambient_matrix,
+                                   double* tangent_matrix) const override {
+    return true;
+  }
+
+  bool Minus(const double* y,
+             const double* x,
+             double* y_minus_x) const override {
+    return true;
+  }
+
+  bool MinusJacobian(const double* x, double* jacobian) const override {
+    return true;
+  }
+};
+
+TEST(ProductManifold, NonDefaultConstructible) {
+  ProductManifold<NonDefaultConstructibleManifold, QuaternionManifold>
+      manifold1{NonDefaultConstructibleManifold{1, 2}, QuaternionManifold{}};
+  ProductManifold<QuaternionManifold, NonDefaultConstructibleManifold>
+      manifold2{QuaternionManifold{}, NonDefaultConstructibleManifold{1, 2}};
+
+  EXPECT_EQ(manifold1.AmbientSize(), manifold2.AmbientSize());
+  EXPECT_EQ(manifold1.TangentSize(), manifold2.TangentSize());
+}
+
+TEST(ProductManifold, DefaultConstructible) {
+  ProductManifold<EuclideanManifold<3>, SphereManifold<4>> manifold1;
+  ProductManifold<SphereManifold<4>, EuclideanManifold<3>> manifold2;
+
+  EXPECT_EQ(manifold1.AmbientSize(), manifold2.AmbientSize());
+  EXPECT_EQ(manifold1.TangentSize(), manifold2.TangentSize());
+}
+
 TEST(QuaternionManifold, PlusPiBy2) {
   QuaternionManifold manifold;
   Vector x = Vector::Zero(4);