diff --git a/include/aidge/operator/Fold.hpp b/include/aidge/operator/Fold.hpp
index 4fa16305a20910969cfa2d33f35d6bdc422f6564..28127f9efe437531a64d228f7ed9c168edc39eb6 100644
--- a/include/aidge/operator/Fold.hpp
+++ b/include/aidge/operator/Fold.hpp
@@ -34,35 +34,33 @@ enum class FoldAttr { OutputDims, StrideDims, DilationDims, KernelDims };
 
 template <DimIdx_t DIM>
 class Fold_Op : public OperatorTensor,
-                public Registrable<Fold_Op<DIM>, std::string, std::shared_ptr<OperatorImpl>(const Fold_Op<DIM> &)>,
-                public StaticAttributes<FoldAttr,
-                                        std::array<DimSize_t, DIM>,
-                                        std::array<DimSize_t, DIM>,
-                                        std::array<DimSize_t, DIM>,
-                                        std::array<DimSize_t, DIM>> {
+                public Registrable<Fold_Op<DIM>, std::string, std::shared_ptr<OperatorImpl>(const Fold_Op<DIM> &)> {
 
 public:
     static const std::string Type;
 
-    Fold_Op() = delete;
-
+private:
     using Attributes_ = StaticAttributes<FoldAttr,
                                         std::array<DimSize_t, DIM>,
                                         std::array<DimSize_t, DIM>,
                                         std::array<DimSize_t, DIM>,
                                         std::array<DimSize_t, DIM>>;
-    template <FoldAttr e>
-    using attr = typename Attributes_::template attr<e>;
+    template <FoldAttr e> using attr = typename Attributes_::template attr<e>;
+    const std::shared_ptr<Attributes_> mAttributes;
+
+public:
+    Fold_Op() = delete;
 
     constexpr Fold_Op(const std::array<DimSize_t, DIM> &outputDims,
                       const std::array<DimSize_t, DIM> &kernelDims,
                       const std::array<DimSize_t, DIM> &strideDims = create_array<DimSize_t,DIM>(1),
                       const std::array<DimSize_t, DIM> &dilationDims = create_array<DimSize_t,DIM>(1))
         : OperatorTensor(Type, {InputCategory::Data}, 1),
-          Attributes_(attr<FoldAttr::OutputDims>(outputDims),
-                      attr<FoldAttr::StrideDims>(strideDims),
-                      attr<FoldAttr::DilationDims>(dilationDims),
-                      attr<FoldAttr::KernelDims>(kernelDims)) {}
+          mAttributes(std::make_shared<Attributes_>(
+            attr<FoldAttr::OutputDims>(outputDims),
+            attr<FoldAttr::StrideDims>(strideDims),
+            attr<FoldAttr::DilationDims>(dilationDims),
+            attr<FoldAttr::KernelDims>(kernelDims))) {}
 
     /**
      * @brief Copy-constructor. Copy the operator attributes and its output tensor(s), but not its
@@ -71,7 +69,7 @@ public:
      */
     Fold_Op(const Fold_Op<DIM> &op)
         : OperatorTensor(op),
-          Attributes_(op)
+          mAttributes(op.mAttributes)
     {
         if (!op.backend().empty()) {
             SET_IMPL_MACRO(Fold_Op<DIM>, *this, op.backend());
@@ -93,6 +91,12 @@ public:
 
     void setBackend(const std::string &name, DeviceIdx_t device = 0) override;
 
+    inline std::shared_ptr<Attributes> attributes() const override { return mAttributes; }
+    inline std::array<DimSize_t, DIM>& outputDims() const { return mAttributes->template getAttr<FoldAttr::OutputDims>(); }
+    inline std::array<DimSize_t, DIM>& strideDims() const { return mAttributes->template getAttr<FoldAttr::StrideDims>(); }
+    inline std::array<DimSize_t, DIM>& dilationDims() const { return mAttributes->template getAttr<FoldAttr::DilationDims>(); }
+    inline std::array<DimSize_t, DIM>& kernelDims() const { return mAttributes->template getAttr<FoldAttr::KernelDims>(); }
+
     static const std::vector<std::string> getInputsName(){
         return {"data_input"};
     }
diff --git a/include/aidge/operator/Unfold.hpp b/include/aidge/operator/Unfold.hpp
index 778c78da28044e1b6262decb903a8562ad0e5ee1..169fbb05ebeff0e5d38eb9606133d6279cc31cd8 100644
--- a/include/aidge/operator/Unfold.hpp
+++ b/include/aidge/operator/Unfold.hpp
@@ -41,31 +41,30 @@ enum class UnfoldAttr { StrideDims, DilationDims, KernelDims };
 
 template <DimIdx_t DIM>
 class Unfold_Op : public OperatorTensor,
-                public Registrable<Unfold_Op<DIM>, std::string, std::shared_ptr<OperatorImpl>(const Unfold_Op<DIM> &)>,
-                public StaticAttributes<UnfoldAttr,
-                                        std::array<DimSize_t, DIM>,
-                                        std::array<DimSize_t, DIM>,
-                                        std::array<DimSize_t, DIM>> {
+                public Registrable<Unfold_Op<DIM>, std::string, std::shared_ptr<OperatorImpl>(const Unfold_Op<DIM> &)> {
 
 public:
     static const std::string Type;
 
-    Unfold_Op() = delete;
-
+private:
     using Attributes_ = StaticAttributes<UnfoldAttr,
                                         std::array<DimSize_t, DIM>,
                                         std::array<DimSize_t, DIM>,
                                         std::array<DimSize_t, DIM>>;
-    template <UnfoldAttr e>
-    using attr = typename Attributes_::template attr<e>;
+    template <UnfoldAttr e> using attr = typename Attributes_::template attr<e>;
+    const std::shared_ptr<Attributes_> mAttributes;
+
+public:
+    Unfold_Op() = delete;
 
     constexpr Unfold_Op(const std::array<DimSize_t, DIM> &kernelDims,
                       const std::array<DimSize_t, DIM> &strideDims = create_array<DimSize_t,DIM>(1),
                       const std::array<DimSize_t, DIM> &dilationDims = create_array<DimSize_t,DIM>(1))
         : OperatorTensor(Type, {InputCategory::Data}, 1),
-          Attributes_(attr<UnfoldAttr::StrideDims>(strideDims),
-                      attr<UnfoldAttr::DilationDims>(dilationDims),
-                      attr<UnfoldAttr::KernelDims>(kernelDims))
+          mAttributes(std::make_shared<Attributes_>(
+            attr<UnfoldAttr::StrideDims>(strideDims),
+            attr<UnfoldAttr::DilationDims>(dilationDims),
+            attr<UnfoldAttr::KernelDims>(kernelDims)))
     {
         mImpl = std::make_shared<Unfold_OpImpl<DIM>>(*this);  
     }
@@ -77,7 +76,7 @@ public:
      */
     Unfold_Op(const Unfold_Op<DIM> &op)
         : OperatorTensor(op),
-          Attributes_(op)
+          mAttributes(op.mAttributes)
     {
         if (!op.backend().empty()) {
             SET_IMPL_MACRO(Unfold_Op<DIM>, *this, op.backend());
@@ -99,6 +98,11 @@ public:
 
     void setBackend(const std::string &name, DeviceIdx_t device = 0) override;
 
+    inline std::shared_ptr<Attributes> attributes() const override { return mAttributes; }
+    inline std::array<DimSize_t, DIM>& strideDims() const { return mAttributes->template getAttr<UnfoldAttr::StrideDims>(); }
+    inline std::array<DimSize_t, DIM>& dilationDims() const { return mAttributes->template getAttr<UnfoldAttr::DilationDims>(); }
+    inline std::array<DimSize_t, DIM>& kernelDims() const { return mAttributes->template getAttr<UnfoldAttr::KernelDims>(); }
+
     static const std::vector<std::string> getInputsName(){
         return {"data_input"};
     }
diff --git a/src/operator/Fold.cpp b/src/operator/Fold.cpp
index a57e117ad00d623c57d057fad9772e620c279b1b..abe73e54ede0611cb14e24332302c35afa91c2a9 100644
--- a/src/operator/Fold.cpp
+++ b/src/operator/Fold.cpp
@@ -33,14 +33,14 @@ bool Aidge::Fold_Op<DIM>::forwardDims(bool /*allowDataDependency*/) {
         DimSize_t k = 1;
         DimSize_t l = 1;
 
-        for (std::size_t dim = 0; dim < this->template getAttr<FoldAttr::KernelDims>().size() ; ++dim) {
-            const DimSize_t kernelExtent = this->template getAttr<FoldAttr::DilationDims>()[dim] *
-                                                    (this->template getAttr<FoldAttr::KernelDims>()[dim] - 1) + 1;
+        for (std::size_t dim = 0; dim < this->kernelDims().size() ; ++dim) {
+            const DimSize_t kernelExtent = this->dilationDims()[dim] *
+                                                    (this->kernelDims()[dim] - 1) + 1;
 
-            k *= this->template getAttr<FoldAttr::KernelDims>()[dim];
+            k *= this->kernelDims()[dim];
             l *= 1 + static_cast<DimSize_t>(
-                    floor(static_cast<float>(this->template getAttr<FoldAttr::OutputDims>()[dim] - kernelExtent) /
-                            static_cast<float>(this->template getAttr<FoldAttr::StrideDims>()[dim])));
+                    floor(static_cast<float>(this->outputDims()[dim] - kernelExtent) /
+                            static_cast<float>(this->strideDims()[dim])));
         }
 
         AIDGE_ASSERT(dims[dims.size() - 2] % k == 0 , "Fold: input number of channels ({}) is not divisible by the product of provided kernel dims ({})!",
@@ -50,7 +50,7 @@ bool Aidge::Fold_Op<DIM>::forwardDims(bool /*allowDataDependency*/) {
 
         dims[dims.size() - 2] /= k;
         dims.pop_back();
-        dims.insert(dims.end(), this->template getAttr<FoldAttr::OutputDims>().begin(), this->template getAttr<FoldAttr::OutputDims>().end());
+        dims.insert(dims.end(), this->outputDims().begin(), this->outputDims().end());
         mOutputs[0]->resize(dims);
         return true;
     }
diff --git a/src/operator/Unfold.cpp b/src/operator/Unfold.cpp
index f15bdd799cb5c75404dc8b9b94dbfee6474b147a..94c970fd3a246f0d9e1237e7cce0c15dd8e24526 100644
--- a/src/operator/Unfold.cpp
+++ b/src/operator/Unfold.cpp
@@ -26,23 +26,23 @@
 template <Aidge::DimIdx_t DIM>
 void Aidge::Unfold_OpImpl<DIM>::forward() {
     const Unfold_Op<DIM>& op = dynamic_cast<const Unfold_Op<DIM>&>(mOp);
-    const auto kernelDims = op.template getAttr<UnfoldAttr::KernelDims>();
-    const auto dilationDims = op.template getAttr<UnfoldAttr::DilationDims>();
-    const auto strideDims = op.template getAttr<UnfoldAttr::StrideDims>();
+    const auto kernelDims = op.kernelDims();
+    const auto dilationDims = op.dilationDims();
+    const auto strideDims = op.strideDims();
     const DimSize_t inHeight = op.getInput(0)->dims()[2];
     const DimSize_t inWidth = op.getInput(0)->dims()[3];
     const DimSize_t inChannels = op.getInput(0)->dims()[1];
 
-    const DimSize_t kernelExtentHeight = op.template getAttr<UnfoldAttr::DilationDims>()[0] *
-                                            (op.template getAttr<UnfoldAttr::KernelDims>()[0] - 1) + 1;
+    const DimSize_t kernelExtentHeight = op.dilationDims()[0] *
+                                            (op.kernelDims()[0] - 1) + 1;
     const DimSize_t outHeight = 1 + static_cast<DimSize_t>(
                     floor(static_cast<float>(inHeight - kernelExtentHeight) /
-                            static_cast<float>(op.template getAttr<UnfoldAttr::StrideDims>()[0])));
-    const DimSize_t kernelExtentWidth = op.template getAttr<UnfoldAttr::DilationDims>()[1] *
-                                            (op.template getAttr<UnfoldAttr::KernelDims>()[1] - 1) + 1;
+                            static_cast<float>(op.strideDims()[0])));
+    const DimSize_t kernelExtentWidth = op.dilationDims()[1] *
+                                            (op.kernelDims()[1] - 1) + 1;
     const DimSize_t outWidth = 1 + static_cast<DimSize_t>(
                     floor(static_cast<float>(inWidth - kernelExtentWidth) /
-                            static_cast<float>(op.template getAttr<UnfoldAttr::StrideDims>()[1])));
+                            static_cast<float>(op.strideDims()[1])));
     const DimSize_t outChannels = op.getOutput(0)->dims()[1];
 
     for (DimSize_t n = 0; n < op.getOutput(0)->dims()[0]; ++n) {
@@ -75,14 +75,14 @@ bool Aidge::Unfold_Op<DIM>::forwardDims(bool /*allowDataDependency*/) {
         DimSize_t k = 1;
         DimSize_t l = 1;
 
-        for (std::size_t dim = 0; dim < this->template getAttr<UnfoldAttr::KernelDims>().size() ; ++dim) {
-            const DimSize_t kernelExtent = this->template getAttr<UnfoldAttr::DilationDims>()[dim] *
-                                                    (this->template getAttr<UnfoldAttr::KernelDims>()[dim] - 1) + 1;
+        for (std::size_t dim = 0; dim < this->kernelDims().size() ; ++dim) {
+            const DimSize_t kernelExtent = this->dilationDims()[dim] *
+                                                    (this->kernelDims()[dim] - 1) + 1;
 
-            k *= this->template getAttr<UnfoldAttr::KernelDims>()[dim];
+            k *= this->kernelDims()[dim];
             l *= 1 + static_cast<DimSize_t>(
                     floor(static_cast<float>(inputDims[dim+2] - kernelExtent) /
-                            static_cast<float>(this->template getAttr<UnfoldAttr::StrideDims>()[dim])));
+                            static_cast<float>(this->strideDims()[dim])));
         }
 
         mOutputs[0]->resize({inputDims[0], inputDims[1] * k, l});
diff --git a/src/recipes/ConvToMatMul.cpp b/src/recipes/ConvToMatMul.cpp
index 75080e90ddc9dff96ceb3b2f1be66f0fc659e4b4..9b88ffc73204b44cf857213d1fdfff49b3191f73 100644
--- a/src/recipes/ConvToMatMul.cpp
+++ b/src/recipes/ConvToMatMul.cpp
@@ -41,10 +41,10 @@ size_t Aidge::convToMatMul(std::shared_ptr<GraphView> graphView) {
         const auto wFlattenSize = std::accumulate(wShape.cbegin() + 1, wShape.cend(), DimSize_t(1), std::multiplies<DimSize_t>());
 
         auto microGraph = std::make_shared<GraphView>();
-        auto unfold = Unfold(convOp->getAttr<std::array<DimSize_t, 2>>("KernelDims"),
+        auto unfold = Unfold(convOp->kernelDims(),
             (!convNode->name().empty()) ? convNode->name() + "_unfold" : "",
-            convOp->getAttr<std::array<DimSize_t, 2>>("StrideDims"),
-            convOp->getAttr<std::array<DimSize_t, 2>>("DilationDims"));
+            convOp->strideDims(),
+            convOp->dilationDims());
         auto wReshapeProd = Producer(std::make_shared<Tensor>(Vector<int64_t>{{static_cast<int64_t>(convOp->getInput(1)->dims()[0]), static_cast<int64_t>(wFlattenSize)}}),
             (!convNode->name().empty()) ? convNode->name() + "_w_reshape_shape_prod" : "",
             true);
@@ -59,10 +59,10 @@ size_t Aidge::convToMatMul(std::shared_ptr<GraphView> graphView) {
             false,
             (!convNode->name().empty()) ? convNode->name() + "_reshape" : "");
         //auto fold = Fold(outputDims,
-        //    convOp->getAttr<std::array<DimSize_t, 2>>("KernelDims"),
+        //    convOp->kernelDims(),
         //    (!convNode->name().empty()) ? convNode->name() + "_unfold" : "",
-        //    convOp->getAttr<std::array<DimSize_t, 2>>("StrideDims"),
-        //    convOp->getAttr<std::array<DimSize_t, 2>>("DilationDims"));
+        //    convOp->strideDims(),
+        //    convOp->dilationDims());
 
         wReshapeProd->addChild(wReshape, 0, 1);
         wReshape->addChild(matMul, 0, 0);