diff --git a/include/aidge/operator/AvgPooling.hpp b/include/aidge/operator/AvgPooling.hpp
index 36de6c11a50692cc53ce9a70af4bef81ab0924bd..dfcd0d5b3b4d892f201485e85710d42cd5b71dba 100644
--- a/include/aidge/operator/AvgPooling.hpp
+++ b/include/aidge/operator/AvgPooling.hpp
@@ -169,12 +169,11 @@ template <std::array<DimSize_t, 1>::size_type DIM>
 inline std::shared_ptr<Node> AvgPooling(const std::array<DimSize_t, DIM> &kernel_dims,
                                            const std::string& name = "",
                                            const std::array<DimSize_t, DIM> &stride_dims = create_array<DimSize_t,DIM>(1)) {
-    // FIXME: properly handle default w&b initialization in every cases
     static_assert(DIM<=MaxDim,"Too many kernel dimensions required by AvgPooling, not supported");
-    auto avgPool = std::make_shared<Node>(std::make_shared<AvgPooling_Op<static_cast<DimIdx_t>(DIM)>>(kernel_dims, stride_dims), name);
-    return avgPool;
+    return std::make_shared<Node>(std::make_shared<AvgPooling_Op<static_cast<DimIdx_t>(DIM)>>(kernel_dims, stride_dims), name);
 }
 
+// helper with C-style array instead of std::array for kernel_dims to allow automatic template DIM deduction
 template <DimSize_t DIM>
 inline std::shared_ptr<Node> AvgPooling(
     DimSize_t const (&kernel_dims)[DIM],
diff --git a/include/aidge/operator/Conv.hpp b/include/aidge/operator/Conv.hpp
index 5e6374c488a34fc8b29a5f841f42b8f44d2fc7a6..b1e3e34b0eff681632d90cb8314ebd8c96722eec 100644
--- a/include/aidge/operator/Conv.hpp
+++ b/include/aidge/operator/Conv.hpp
@@ -201,6 +201,7 @@ inline std::shared_ptr<Node> Conv(DimSize_t in_channels,
     return conv;
 }
 
+// helper with C-style array instead of std::array for kernel_dims to allow automatic template DIM deduction
 template <DimSize_t DIM>
 inline std::shared_ptr<Node> Conv(
     DimSize_t in_channels,
diff --git a/include/aidge/operator/ConvDepthWise.hpp b/include/aidge/operator/ConvDepthWise.hpp
index ec8ce2b3e1e2961658bd5fce7342fe5a31b7bb5b..4caec2032a3c61529d452ae855f00c1da411af10 100644
--- a/include/aidge/operator/ConvDepthWise.hpp
+++ b/include/aidge/operator/ConvDepthWise.hpp
@@ -197,6 +197,7 @@ inline std::shared_ptr<Node> ConvDepthWise(const std::array<DimSize_t, DIM> &ker
     return convDW;
 }
 
+// helper with C-style array instead of std::array for kernel_dims to allow automatic template DIM deduction
 template <DimSize_t DIM>
 inline std::shared_ptr<Node> ConvDepthWise(
     DimSize_t const (&kernel_dims)[DIM],
diff --git a/include/aidge/operator/LeakyReLU.hpp b/include/aidge/operator/LeakyReLU.hpp
index 40d9959b3802dcbe337adeafa9643bf0682df64b..bcdcbc7cabd8eda46a7c0c4930f317e562fb46a0 100644
--- a/include/aidge/operator/LeakyReLU.hpp
+++ b/include/aidge/operator/LeakyReLU.hpp
@@ -146,7 +146,6 @@ public:
 };
 
 inline std::shared_ptr<Node> LeakyReLU(float negativeSlope = 0.0f, const std::string& name = "") {
-    // FIXME: properly handle default w&b initialization in every cases
     return std::make_shared<Node>(std::make_shared<LeakyReLU_Op>(negativeSlope), name);
 }
 }
diff --git a/include/aidge/operator/MaxPooling.hpp b/include/aidge/operator/MaxPooling.hpp
index e261fb4b8c6d1448cca010f6aa11214bf6597f67..874ea81778e0b357a4890b6bb052e85fa266216e 100644
--- a/include/aidge/operator/MaxPooling.hpp
+++ b/include/aidge/operator/MaxPooling.hpp
@@ -170,12 +170,11 @@ template <std::array<DimSize_t, 1>::size_type DIM>
 inline std::shared_ptr<Node> MaxPooling(const std::array<DimSize_t, DIM> &kernel_dims,
                                            const std::string& name = "",
                                            const std::array<DimSize_t, DIM> &stride_dims = create_array<DimSize_t,DIM>(1)) {
-    // FIXME: properly handle default w&b initialization in every cases
     static_assert(DIM<=MaxDim,"Too many kernel dimensions required by MaxPooling, not supported");
-    auto avgPool = std::make_shared<Node>(std::make_shared<MaxPooling_Op<static_cast<DimIdx_t>(DIM)>>(kernel_dims, stride_dims), name);
-    return avgPool;
+    return std::make_shared<Node>(std::make_shared<MaxPooling_Op<static_cast<DimIdx_t>(DIM)>>(kernel_dims, stride_dims), name);
 }
 
+// helper with C-style array instead of std::array for kernel_dims to allow automatic template DIM deduction
 template <DimSize_t DIM>
 inline std::shared_ptr<Node> MaxPooling(
     DimSize_t const (&kernel_dims)[DIM],
diff --git a/include/aidge/operator/MetaOperator.hpp b/include/aidge/operator/MetaOperator.hpp
index bb34fd9c7756f103d4f31f17f815309c925306b7..72058dfcba6e811a01a22e261208741879638cad 100644
--- a/include/aidge/operator/MetaOperator.hpp
+++ b/include/aidge/operator/MetaOperator.hpp
@@ -156,9 +156,11 @@ public:
 
 inline std::shared_ptr<Node> MetaOperator(const char *type,
                                   const std::shared_ptr<GraphView>& graph,
-                                  const std::string& name = "")
+                                  const std::string& name = "",
+                                  std::vector<NodePtr> inputNodes = std::vector<NodePtr>(),
+                                  std::vector<NodePtr> outputNodes = std::vector<NodePtr>())
 {
-    return std::make_shared<Node>(std::make_shared<MetaOperator_Op>(type, graph), name);
+    return std::make_shared<Node>(std::make_shared<MetaOperator_Op>(type, graph, inputNodes, outputNodes), name);
 }
 }  // namespace Aidge
 
diff --git a/include/aidge/operator/MetaOperatorDefs.hpp b/include/aidge/operator/MetaOperatorDefs.hpp
index df66cec7e1accfee1518378ce2e9697cdc7f91fb..e342e0343c69f941cbd1ebf8e0d826b1b476fbb0 100644
--- a/include/aidge/operator/MetaOperatorDefs.hpp
+++ b/include/aidge/operator/MetaOperatorDefs.hpp
@@ -26,21 +26,22 @@ inline std::shared_ptr<Node> PaddedConv(DimSize_t in_channels,
                                   const std::array<DimSize_t, DIM> &kernel_dims,
                                   const std::string& name = "",
                                   const std::array<DimSize_t, DIM> &stride_dims = create_array<DimSize_t,DIM>(1),
-                                  const std::array<std::array<DimSize_t, 2>, DIM> &padding_dims = {0},
+                                  const std::array<DimSize_t, 2*DIM> &padding_dims = create_array<DimSize_t,2*DIM>(0),
                                   const std::array<DimSize_t, DIM> &dilation_dims = create_array<DimSize_t,DIM>(1))
 {
     // Construct micro-graph
-    auto pad = std::make_shared<Node>(std::make_shared<Pad_Op<static_cast<DimIdx_t>(DIM)>>(padding_dims, PadBorderType::Constant, 0.0), (!name.empty()) ? name + "_pad" : "");
+    auto pad = Pad(padding_dims, (!name.empty()) ? name + "_pad" : "", PadBorderType::Constant, 0.0);
     auto conv = std::make_shared<Node>(std::make_shared<Conv_Op<static_cast<DimIdx_t>(DIM)>>(in_channels, out_channels, kernel_dims, stride_dims, dilation_dims), (!name.empty()) ? name + "_conv" : "");
     // Need to specify the ordered list of input operators
     const std::vector<NodePtr> orderedInputNodes = {pad, conv};
 
-    auto metaOp = std::make_shared<Node>(std::make_shared<MetaOperator_Op>("PaddedConv", Sequential({pad, conv}), orderedInputNodes), name);
+    auto metaOp = MetaOperator("PaddedConv", Sequential({pad, conv}), name, orderedInputNodes);
     addProducer(metaOp, 1, append(out_channels, append(in_channels, kernel_dims)), "w");
     addProducer(metaOp, 2, {out_channels}, "b");
     return metaOp;
 }
 
+// helper with C-style array instead of std::array for kernel_dims to allow automatic template DIM deduction
 template <DimSize_t DIM>
 inline std::shared_ptr<Node> PaddedConv(
     DimSize_t in_channels,
@@ -48,76 +49,91 @@ inline std::shared_ptr<Node> PaddedConv(
     DimSize_t const (&kernel_dims)[DIM],
     const std::string& name = "",
     const std::array<DimSize_t, DIM> &stride_dims = create_array<DimSize_t,DIM>(1),
-    const std::array<std::array<DimSize_t, 2>, DIM> &padding_dims = {0},
+    const std::array<DimSize_t, 2*DIM> &padding_dims = create_array<DimSize_t,2*DIM>(0),
     const std::array<DimSize_t, DIM> &dilation_dims = create_array<DimSize_t,DIM>(1))
 {
-    return PaddedConv<DIM>(in_channels, out_channels, to_array(kernel_dims), name, stride_dims, padding_dims, dilation_dims);
+    return PaddedConv(in_channels, out_channels, to_array(kernel_dims), name, stride_dims, padding_dims, dilation_dims);
 }
 
 template <std::array<DimSize_t, 1>::size_type DIM>
-inline std::shared_ptr<Node> PaddedConvDepthWise(DimSize_t in_channels,
-                                  DimSize_t out_channels,
-                                  const std::array<DimSize_t, DIM> &kernel_dims,
+inline std::shared_ptr<Node> PaddedConvDepthWise(const std::array<DimSize_t, DIM> &kernel_dims,
                                   const std::string& name = "",
                                   const std::array<DimSize_t, DIM> &stride_dims = create_array<DimSize_t,DIM>(1),
-                                  const std::array<std::array<DimSize_t, 2>, DIM> &padding_dims = {0},
+                                  const std::array<DimSize_t, 2*DIM> &padding_dims = create_array<DimSize_t,2*DIM>(0),
                                   const std::array<DimSize_t, DIM> &dilation_dims = create_array<DimSize_t,DIM>(1))
 {
     // Construct micro-graph
-    auto pad = std::make_shared<Node>(std::make_shared<Pad_Op<static_cast<DimIdx_t>(DIM)>>(padding_dims, PadBorderType::Constant, 0.0), (!name.empty()) ? name + "_pad" : "");
-    auto conv = std::make_shared<Node>(std::make_shared<ConvDepthWise_Op<static_cast<DimIdx_t>(DIM)>>(in_channels, out_channels, kernel_dims, stride_dims, dilation_dims), (!name.empty()) ? name + "_conv" : "");
+    auto pad = Pad(padding_dims, (!name.empty()) ? name + "_pad" : "", PadBorderType::Constant, 0.0);
+    auto conv = std::make_shared<Node>(std::make_shared<ConvDepthWise_Op<static_cast<DimIdx_t>(DIM)>>(kernel_dims, stride_dims, dilation_dims), (!name.empty()) ? name + "_conv" : "");
     // Need to specify the ordered list of input operators
     const std::vector<NodePtr> orderedInputNodes = {pad, conv};
 
-    auto metaOp = std::make_shared<Node>(std::make_shared<MetaOperator_Op>("PaddedConvDepthWise", Sequential({pad, conv}), orderedInputNodes), name);
-    addProducer(metaOp, 1, append(out_channels, append(in_channels, kernel_dims)), "w");
-    addProducer(metaOp, 2, {out_channels}, "b");
+    auto metaOp = MetaOperator("PaddedConvDepthWise", Sequential({pad, conv}), name, orderedInputNodes);
+    addProducer(metaOp, 1, std::array<DimSize_t,0>({}), "w");
+    addProducer(metaOp, 2, std::array<DimSize_t,0>({}), "b");
     return metaOp;
 }
 
+// helper with C-style array instead of std::array for kernel_dims to allow automatic template DIM deduction
 template <DimSize_t DIM>
 inline std::shared_ptr<Node> PaddedConvDepthWise(
-    DimSize_t in_channels,
-    DimSize_t out_channels,
     DimSize_t const (&kernel_dims)[DIM],
     const std::string& name = "",
     const std::array<DimSize_t, DIM> &stride_dims = create_array<DimSize_t,DIM>(1),
-    const std::array<std::array<DimSize_t, 2>, DIM> &padding_dims = {0},
+    const std::array<DimSize_t, 2*DIM> &padding_dims = create_array<DimSize_t,2*DIM>(0),
     const std::array<DimSize_t, DIM> &dilation_dims = create_array<DimSize_t,DIM>(1))
 {
-    return PaddedConvDepthWise<DIM>(in_channels, out_channels, to_array(kernel_dims), name, stride_dims, padding_dims, dilation_dims);
+    return PaddedConvDepthWise(to_array(kernel_dims), name, stride_dims, padding_dims, dilation_dims);
 }
 
 template <std::array<DimSize_t, 1>::size_type DIM>
-inline std::shared_ptr<Node> PaddedAvgPooling(DimSize_t in_channels,
-                                  DimSize_t out_channels,
-                                  const std::array<DimSize_t, DIM> &kernel_dims,
+inline std::shared_ptr<Node> PaddedAvgPooling(const std::array<DimSize_t, DIM> &kernel_dims,
                                   const std::string& name = "",
                                   const std::array<DimSize_t, DIM> &stride_dims = create_array<DimSize_t,DIM>(1),
-                                  const std::array<std::array<DimSize_t, 2>, DIM> &padding_dims = {0})
+                                  const std::array<DimSize_t, 2*DIM> &padding_dims = create_array<DimSize_t,2*DIM>(0))
 {
     auto graph = Sequential({
-        Pad<DIM>(padding_dims, (!name.empty()) ? name + "_pad" : ""),
-        AvgPooling_Op<DIM>(kernel_dims, (!name.empty()) ? name + "_avgpooling" : "", stride_dims)
+        Pad(padding_dims, (!name.empty()) ? name + "_pad" : ""),
+        AvgPooling(kernel_dims, (!name.empty()) ? name + "_avgpooling" : "", stride_dims)
     });
 
-    return std::make_shared<Node>(std::make_shared<MetaOperator_Op>("PaddedAvgPooling", graph), name);
+    return MetaOperator("PaddedAvgPooling", graph, name);
+}
+
+// helper with C-style array instead of std::array for kernel_dims to allow automatic template DIM deduction
+template <DimSize_t DIM>
+inline std::shared_ptr<Node> PaddedAvgPooling(
+    DimSize_t const (&kernel_dims)[DIM],
+    const std::string& name = "",
+    const std::array<DimSize_t, DIM> &stride_dims = create_array<DimSize_t,DIM>(1),
+    const std::array<DimSize_t, 2*DIM> &padding_dims = create_array<DimSize_t,2*DIM>(0))
+{
+    return PaddedAvgPooling(to_array(kernel_dims), name, stride_dims, padding_dims);
 }
 
 template <std::array<DimSize_t, 1>::size_type DIM>
-inline std::shared_ptr<Node> PaddedMaxPooling(DimSize_t in_channels,
-                                  DimSize_t out_channels,
-                                  const std::array<DimSize_t, DIM> &kernel_dims,
+inline std::shared_ptr<Node> PaddedMaxPooling(const std::array<DimSize_t, DIM> &kernel_dims,
                                   const std::string& name = "",
                                   const std::array<DimSize_t, DIM> &stride_dims = create_array<DimSize_t,DIM>(1),
-                                  const std::array<std::array<DimSize_t, 2>, DIM> &padding_dims = {0})
+                                  const std::array<DimSize_t, 2*DIM> &padding_dims = create_array<DimSize_t,2*DIM>(0))
 {
     auto graph = Sequential({
-        Pad<DIM>(padding_dims, (!name.empty()) ? name + "_pad" : ""),
-        MaxPooling_Op<DIM>(kernel_dims, (!name.empty()) ? name + "_maxpooling" : "", stride_dims)
+        Pad(padding_dims, (!name.empty()) ? name + "_pad" : ""),
+        MaxPooling(kernel_dims, (!name.empty()) ? name + "_maxpooling" : "", stride_dims)
     });
 
-    return std::make_shared<Node>(std::make_shared<MetaOperator_Op>("PaddedMaxPooling", graph), name);
+    return MetaOperator("PaddedMaxPooling", graph, name);
+}
+
+// helper with C-style array instead of std::array for kernel_dims to allow automatic template DIM deduction
+template <DimSize_t DIM>
+inline std::shared_ptr<Node> PaddedMaxPooling(
+    DimSize_t const (&kernel_dims)[DIM],
+    const std::string& name = "",
+    const std::array<DimSize_t, DIM> &stride_dims = create_array<DimSize_t,DIM>(1),
+    const std::array<DimSize_t, 2*DIM> &padding_dims = create_array<DimSize_t,2*DIM>(0))
+{
+    return PaddedMaxPooling(to_array(kernel_dims), name, stride_dims, padding_dims);
 }
 }  // namespace Aidge
 
diff --git a/include/aidge/operator/Pad.hpp b/include/aidge/operator/Pad.hpp
index ddc611a0fc6e3604ad6cb1949142d26625ae778a..727d525f68d75d94185d4fe6dc2f36951f63fe55 100644
--- a/include/aidge/operator/Pad.hpp
+++ b/include/aidge/operator/Pad.hpp
@@ -27,13 +27,13 @@
 
 namespace Aidge {
 enum class PadAttr { BeginEndBorders, BorderType, BorderValue };
-enum class PadBorderType { Constant, Replicate, Reflect, Wrap };
+enum class PadBorderType { Constant, Edge, Reflect, Wrap };
 
 template <DimIdx_t DIM>
 class Pad_Op : public Operator,
                 public Registrable<Pad_Op<DIM>, std::string, std::unique_ptr<OperatorImpl>(const Pad_Op<DIM> &)>,
                 public StaticAttributes<PadAttr,
-                                       std::array<std::array<DimSize_t, 2>, DIM>,
+                                       std::array<DimSize_t, 2*DIM>,
                                        PadBorderType,
                                        double> {
 private:
@@ -47,13 +47,13 @@ public:
     Pad_Op() = delete;
 
     using Attributes_ = StaticAttributes<PadAttr,
-                                             std::array<std::array<DimSize_t, 2>, DIM>,
+                                             std::array<DimSize_t, 2*DIM>,
                                              PadBorderType,
                                              double>;
     template <PadAttr e>
     using attr = typename Attributes_::template attr<e>;
 
-    constexpr Pad_Op(const std::array<std::array<DimSize_t, 2>, DIM> &beginEndTuples,
+    constexpr Pad_Op(const std::array<DimSize_t, 2*DIM> &beginEndTuples,
                      const PadBorderType &borderType = PadBorderType::Constant,
                      double borderValue = 0.0)
         : Operator(Type),
@@ -97,9 +97,9 @@ public:
             std::array<DimSize_t, DIM + 2> outputDims = {};
 
             for (std::size_t dim = 0; dim < DIM; ++dim) {
-                outputDims[dim+2] = this->template getAttr<PadAttr::BeginEndBorders>()[dim][0]
+                outputDims[dim+2] = this->template getAttr<PadAttr::BeginEndBorders>()[2*dim]
                                     + mInput->dims()[dim+2]
-                                    + this->template getAttr<PadAttr::BeginEndBorders>()[dim][1];
+                                    + this->template getAttr<PadAttr::BeginEndBorders>()[2*dim+1];
             }
             outputDims[1] = mInput->dims()[1];
             outputDims[0] = mInput->dims()[0];
@@ -168,63 +168,29 @@ public:
     }
 };
 
-template <std::array<DimSize_t, 1>::size_type DIM>
-inline std::shared_ptr<Node> Pad(const std::array<std::array<DimSize_t, 2>, DIM> &beginEndTuples,
+// We use DIMX2 rather than DIM because the compiler cannot infer DIM from std::array<DimSize_t, 2*DIM>
+template <std::array<DimSize_t, 1>::size_type DIMX2>
+inline std::shared_ptr<Node> Pad(const std::array<DimSize_t, DIMX2> &beginEndTuples,
                                            const std::string& name = "",
                                            const PadBorderType &borderType = PadBorderType::Constant,
                                            double borderValue = 0.0)
 {
-    static_assert(DIM<=MaxDim,"Too many kernel dimensions required by Pad, not supported");
-    auto pad = std::make_shared<Node>(std::make_shared<Pad_Op<static_cast<DimIdx_t>(DIM)>>(beginEndTuples, borderType, borderValue), name);
-    return pad;
+    static_assert(DIMX2%2==0,"Invalid dimension");
+    static_assert(DIMX2/2<=MaxDim,"Too many kernel dimensions required by Pad, not supported");
+    return std::make_shared<Node>(std::make_shared<Pad_Op<static_cast<DimIdx_t>(DIMX2/2)>>(beginEndTuples, borderType, borderValue), name);
 }
 
-template <std::array<DimSize_t, 1>::size_type DIM>
-inline std::shared_ptr<Node> Pad(const std::array<DimSize_t, DIM> &dimBeginEnd,
-                                           const std::string& name = "",
-                                           const PadBorderType &borderType = PadBorderType::Constant,
-                                           double borderValue = 0.0)
-{
-    static_assert(DIM<=MaxDim,"Too many kernel dimensions required by Pad, not supported");
-    std::array<std::array<DimSize_t, 2>, DIM> beginEndTuples;
-    for (size_t i = 0; i < DIM; ++i) {
-        beginEndTuples[i] = {dimBeginEnd[i], dimBeginEnd[i]};
-    }
-    auto pad = std::make_shared<Node>(std::make_shared<Pad_Op<static_cast<DimIdx_t>(DIM)>>(beginEndTuples, borderType, borderValue), name);
-    return pad;
-}
-
-template <std::array<DimSize_t, 1>::size_type DIM>
-inline std::shared_ptr<Node> ZeroPad(const std::array<std::array<DimSize_t, 2>, DIM> &beginEndTuples,
-                                           const std::string& name = "")
-{
-    static_assert(DIM<=MaxDim,"Too many kernel dimensions required by Pad, not supported");
-    auto pad = std::make_shared<Node>(std::make_shared<Pad_Op<static_cast<DimIdx_t>(DIM)>>(beginEndTuples, PadBorderType::Constant, 0.0), name);
-    return pad;
-}
-
-template <std::array<DimSize_t, 1>::size_type DIM>
-inline std::shared_ptr<Node> ZeroPad(const std::array<DimSize_t, DIM> &dimBeginEnd,
-                                           const std::string& name = "")
-{
-    static_assert(DIM<=MaxDim,"Too many kernel dimensions required by Pad, not supported");
-    std::array<std::array<DimSize_t, 2>, DIM> beginEndTuples;
-    for (size_t i = 0; i < DIM; ++i) {
-        beginEndTuples[i] = {dimBeginEnd[i], dimBeginEnd[i]};
-    }
-    auto pad = std::make_shared<Node>(std::make_shared<Pad_Op<static_cast<DimIdx_t>(DIM)>>(beginEndTuples, PadBorderType::Constant, 0.0), name);
-    return pad;
-}
-
-template <DimSize_t DIM>
+// helper with C-style array instead of std::array for beginEndTuples to allow automatic template DIM deduction
+template <DimSize_t DIMX2>
 inline std::shared_ptr<Node> Pad(
-    std::array<DimSize_t, 2> const (&beginEndTuples)[DIM],
+    DimSize_t const (&beginEndTuples)[DIMX2],
     const std::string& name = "",
     const PadBorderType &borderType = PadBorderType::Constant,
     double borderValue = 0.0)
 {
-    static_assert(DIM<=MaxDim,"Too many kernel dimensions required by Pad, not supported");
-    return Pad(to_array(beginEndTuples), name, borderType, borderValue);
+    static_assert(DIMX2%2==0,"Invalid dimension");
+    static_assert(DIMX2/2<=MaxDim,"Too many kernel dimensions required by Pad, not supported");
+    return Pad<DIMX2/2>(to_array(beginEndTuples), name, borderType, borderValue);
 }
 }  // namespace Aidge
 
@@ -233,7 +199,7 @@ template <>
 const char *const EnumStrings<Aidge::PadAttr>::data[] = {"BeginEndBorders", "BorderType", "BorderValue"};
 
 template <>
-const char *const EnumStrings<Aidge::PadBorderType>::data[] = {"Constant", "Replicate", "Reflect", "Wrap"};
+const char *const EnumStrings<Aidge::PadBorderType>::data[] = {"Constant", "Edge", "Reflect", "Wrap"};
 }
 
 #endif /* AIDGE_CORE_OPERATOR_PAD_H_ */
diff --git a/include/aidge/operator/Producer.hpp b/include/aidge/operator/Producer.hpp
index 529a37c063567e2e09367176437f212c69b2bf40..c263ae139f11503fbc8a7682e53ccf536cbe5e98 100644
--- a/include/aidge/operator/Producer.hpp
+++ b/include/aidge/operator/Producer.hpp
@@ -154,6 +154,7 @@ inline std::shared_ptr<Node> Producer(const std::array<DimSize_t, DIM> &dims, co
   return std::make_shared<Node>(std::make_shared<Producer_Op>(dims), name);
 }
 
+// helper with C-style array instead of std::array for kernel_dims to allow automatic template DIM deduction
 template <std::size_t DIM>
 inline std::shared_ptr<Node> Producer(DimSize_t const (&dims)[DIM], const std::string& name = "") {
   return Producer(to_array(dims), name);
@@ -173,6 +174,7 @@ void addProducer(std::shared_ptr<Node>& otherNode, const IOIndex_t inputIdx, con
     otherNode->getOperator()->associateInput(inputIdx, prod->getOperator()->getRawOutput(0));
 }
 
+// helper with C-style array instead of std::array for kernel_dims to allow automatic template DIM deduction
 template <std::size_t DIM>
 void addProducer(std::shared_ptr<Node>& otherNode, const IOIndex_t inputIdx, DimSize_t const (&dims)[DIM], const std::string& extension) {
     addProducer(otherNode, inputIdx, to_array(dims), extension);
diff --git a/include/aidge/operator/ReLU.hpp b/include/aidge/operator/ReLU.hpp
index 07f79f64933b3eec9e98a5ad13a4afbda9aed588..52f13f1c5ce1d0b7a0d4ccaa4d7fe9927bcc3e53 100644
--- a/include/aidge/operator/ReLU.hpp
+++ b/include/aidge/operator/ReLU.hpp
@@ -134,7 +134,6 @@ public:
 };
 
 inline std::shared_ptr<Node> ReLU(const std::string& name = "") {
-    // FIXME: properly handle default w&b initialization in every cases
     return std::make_shared<Node>(std::make_shared<ReLU_Op>(), name);
 }
 }
diff --git a/include/aidge/operator/Softmax.hpp b/include/aidge/operator/Softmax.hpp
index e6c9869a50d1d142c20525ce2ccfc4f1de5088ed..ba6132a5ee00325d0f7de57db117a169d42352e9 100644
--- a/include/aidge/operator/Softmax.hpp
+++ b/include/aidge/operator/Softmax.hpp
@@ -134,7 +134,6 @@ public:
 };
 
 inline std::shared_ptr<Node> Softmax(const std::string& name = "") {
-    // FIXME: properly handle default w&b initialization in every cases
     return std::make_shared<Node>(std::make_shared<Softmax_Op>(), name);
 }
 }
diff --git a/include/aidge/utils/DynamicAttributes.hpp b/include/aidge/utils/DynamicAttributes.hpp
index af03ee2861e81d81171ccc2ea14289f2ce3aa9e3..2af8f47e9420f266cc6eca21f167944c761db7ea 100644
--- a/include/aidge/utils/DynamicAttributes.hpp
+++ b/include/aidge/utils/DynamicAttributes.hpp
@@ -19,7 +19,7 @@
 #include <cassert>
 #include <string>
 
-#include "aidge/utils/Any.hpp"
+#include "aidge/utils/future_std/any.hpp"
 #include "aidge/utils/Attributes.hpp"
 
 #ifdef PYBIND
@@ -54,12 +54,12 @@ public:
             auto itPy = mAttrsPy.find(name);
             if (itPy != mAttrsPy.end()) {
                 // Insert the attribute back in C++
-                mAttrs.emplace(std::make_pair(name, libany::any(itPy->second.cast<T>())));
+                mAttrs.emplace(std::make_pair(name, future_std::any(itPy->second.cast<T>())));
             }
         }
 #endif
 
-        return libany::any_cast<T&>(mAttrs.at(name));
+        return future_std::any_cast<T&>(mAttrs.at(name));
     }
 
     template<class T> const T& getAttr(const std::string& name) const
@@ -71,12 +71,12 @@ public:
             auto itPy = mAttrsPy.find(name);
             if (itPy != mAttrsPy.end()) {
                 // Insert the attribute back in C++
-                mAttrs.emplace(std::make_pair(name, libany::any(itPy->second.cast<T>())));
+                mAttrs.emplace(std::make_pair(name, future_std::any(itPy->second.cast<T>())));
             }
         }
 #endif
 
-        return libany::any_cast<const T&>(mAttrs.at(name));
+        return future_std::any_cast<const T&>(mAttrs.at(name));
     }
 
     ///\brief Add a new Attribute, identified by its name. If it already exists, asserts.
@@ -85,7 +85,7 @@ public:
     ///\param value Attribute value
     template<class T> void addAttr(const std::string& name, const T& value)
     {
-        const auto& res = mAttrs.emplace(std::make_pair(name, libany::any(value)));
+        const auto& res = mAttrs.emplace(std::make_pair(name, future_std::any(value)));
         assert(res.second && "attribute already exists");
 
 #ifdef PYBIND
@@ -103,9 +103,9 @@ public:
     ///\param value Attribute value
     template<class T> void setAttr(const std::string& name, const T& value)
     {
-        auto res = mAttrs.emplace(std::make_pair(name, libany::any(value)));
+        auto res = mAttrs.emplace(std::make_pair(name, future_std::any(value)));
         if (!res.second)
-            res.first->second = libany::any(value);
+            res.first->second = future_std::any(value);
 
 #ifdef PYBIND
         // We cannot handle Python object if the Python interpreter is not running
@@ -210,9 +210,9 @@ private:
     std::map<std::string, py::object> mAttrsPy;
     // Stores C++ attributes only
     // mutable because it may be updated in getAttr() from Python
-    mutable std::map<std::string, libany::any> mAttrs;
+    mutable std::map<std::string, future_std::any> mAttrs;
 #else
-    std::map<std::string, libany::any> mAttrs;
+    std::map<std::string, future_std::any> mAttrs;
 #endif
 };
 
diff --git a/include/aidge/utils/Any.hpp b/include/aidge/utils/future_std/any.hpp
similarity index 98%
rename from include/aidge/utils/Any.hpp
rename to include/aidge/utils/future_std/any.hpp
index 0e65710596d31920de60a35d600e7ae612ea2bc4..8d9bfe28d0497dc12c59aaed68a23d3a9563815e 100644
--- a/include/aidge/utils/Any.hpp
+++ b/include/aidge/utils/future_std/any.hpp
@@ -14,11 +14,11 @@
  * Copyright (c) 2018 Claudio Fantacci
  *
  * Distributed under the Boost Software License, Version 1.0.
- * (See accompanying file LICENSE.md or copy at http://www.boost.org/LICENSE_1_0.txt)
+ * (See copy at http://www.boost.org/LICENSE_1_0.txt)
  */
 
-#ifndef AIDGE_CORE_UTILS_ANY_H_
-#define AIDGE_CORE_UTILS_ANY_H_
+#ifndef AIDGE_CORE_UTILS_FUTURE_STD_ANY_H_
+#define AIDGE_CORE_UTILS_FUTURE_STD_ANY_H_
 
 #include <stdexcept>
 #include <typeinfo>
@@ -26,7 +26,7 @@
 #include <utility>
 
 
-namespace libany
+namespace future_std
 {
 
 class bad_any_cast : public std::bad_cast
@@ -549,4 +549,4 @@ inline void swap(any& lhs, any& rhs) noexcept
 
 }
 
-#endif /* AIDGE_CORE_UTILS_ANY_H_ */
+#endif /* AIDGE_CORE_UTILS_FUTURE_STD_ANY_H_ */
diff --git a/include/aidge/utils/future_std/expected.hpp b/include/aidge/utils/future_std/expected.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c271d0e8d8066c0bcd0358f28f8bcd711a8b6ba0
--- /dev/null
+++ b/include/aidge/utils/future_std/expected.hpp
@@ -0,0 +1,3487 @@
+// Origin: https://github.com/martinmoene/expected-lite
+//
+// This version targets C++11 and later.
+//
+// Copyright (C) 2016-2020 Martin Moene.
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// expected lite is based on:
+//   A proposal to add a utility class to represent expected monad
+//   by Vicente J. Botet Escriba and Pierre Talbot. http:://wg21.link/p0323
+
+#ifndef AIDGE_CORE_UTILS_FUTURE_STD_EXPECTED_H_
+#define AIDGE_CORE_UTILS_FUTURE_STD_EXPECTED_H_
+
+#define expected_lite_MAJOR  0
+#define expected_lite_MINOR  6
+#define expected_lite_PATCH  3
+
+#define expected_lite_VERSION  expected_STRINGIFY(expected_lite_MAJOR) "." expected_STRINGIFY(expected_lite_MINOR) "." expected_STRINGIFY(expected_lite_PATCH)
+
+#define expected_STRINGIFY(  x )  expected_STRINGIFY_( x )
+#define expected_STRINGIFY_( x )  #x
+
+// expected-lite configuration:
+
+#define nsel_EXPECTED_DEFAULT  0
+#define nsel_EXPECTED_FUTURE_STD   1
+#define nsel_EXPECTED_STD      2
+
+// tweak header support:
+
+#ifdef __has_include
+# if __has_include(<future_std/expected.tweak.hpp>)
+#  include <future_std/expected.tweak.hpp>
+# endif
+#define expected_HAVE_TWEAK_HEADER  1
+#else
+#define expected_HAVE_TWEAK_HEADER  0
+//# pragma message("expected.hpp: Note: Tweak header not supported.")
+#endif
+
+// expected selection and configuration:
+
+#if !defined( nsel_CONFIG_SELECT_EXPECTED )
+# define nsel_CONFIG_SELECT_EXPECTED  ( nsel_HAVE_STD_EXPECTED ? nsel_EXPECTED_STD : nsel_EXPECTED_FUTURE_STD )
+#endif
+
+// Proposal revisions:
+//
+// DXXXXR0: --
+// N4015  : -2 (2014-05-26)
+// N4109  : -1 (2014-06-29)
+// P0323R0:  0 (2016-05-28)
+// P0323R1:  1 (2016-10-12)
+// -------:
+// P0323R2:  2 (2017-06-15)
+// P0323R3:  3 (2017-10-15)
+// P0323R4:  4 (2017-11-26)
+// P0323R5:  5 (2018-02-08)
+// P0323R6:  6 (2018-04-02)
+// P0323R7:  7 (2018-06-22) *
+//
+// expected-lite uses 2 and higher
+
+#ifndef  nsel_P0323R
+# define nsel_P0323R  7
+#endif
+
+// Monadic operations proposal revisions:
+//
+// P2505R0:  0 (2021-12-12)
+// P2505R1:  1 (2022-02-10)
+// P2505R2:  2 (2022-04-15)
+// P2505R3:  3 (2022-06-05)
+// P2505R4:  4 (2022-06-15)
+// P2505R5:  5 (2022-09-20) *
+//
+// expected-lite uses 5
+
+#ifndef  nsel_P2505R
+# define nsel_P2505R  5
+#endif
+
+// Control presence of C++ exception handling (try and auto discover):
+
+#ifndef nsel_CONFIG_NO_EXCEPTIONS
+# if defined(_MSC_VER)
+#  include <cstddef>    // for _HAS_EXCEPTIONS
+# endif
+# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS)
+#  define nsel_CONFIG_NO_EXCEPTIONS  0
+# else
+#  define nsel_CONFIG_NO_EXCEPTIONS  1
+# endif
+#endif
+
+// at default use SEH with MSVC for no C++ exceptions
+
+#ifndef  nsel_CONFIG_NO_EXCEPTIONS_SEH
+# define nsel_CONFIG_NO_EXCEPTIONS_SEH  ( nsel_CONFIG_NO_EXCEPTIONS && _MSC_VER )
+#endif
+
+// C++ language version detection (C++23 is speculative):
+// Note: VC14.0/1900 (VS2015) lacks too much from C++14.
+
+#ifndef   nsel_CPLUSPLUS
+# if defined(_MSVC_LANG ) && !defined(__clang__)
+#  define nsel_CPLUSPLUS  (_MSC_VER == 1900 ? 201103L : _MSVC_LANG )
+# else
+#  define nsel_CPLUSPLUS  __cplusplus
+# endif
+#endif
+
+#define nsel_CPP98_OR_GREATER  ( nsel_CPLUSPLUS >= 199711L )
+#define nsel_CPP11_OR_GREATER  ( nsel_CPLUSPLUS >= 201103L )
+#define nsel_CPP14_OR_GREATER  ( nsel_CPLUSPLUS >= 201402L )
+#define nsel_CPP17_OR_GREATER  ( nsel_CPLUSPLUS >= 201703L )
+#define nsel_CPP20_OR_GREATER  ( nsel_CPLUSPLUS >= 202002L )
+#define nsel_CPP23_OR_GREATER  ( nsel_CPLUSPLUS >= 202300L )
+
+// Use C++23 std::expected if available and requested:
+
+#if nsel_CPP23_OR_GREATER && defined(__has_include )
+# if __has_include( <expected> )
+#  define nsel_HAVE_STD_EXPECTED  1
+# else
+#  define nsel_HAVE_STD_EXPECTED  0
+# endif
+#else
+# define  nsel_HAVE_STD_EXPECTED  0
+#endif
+
+#define  nsel_USES_STD_EXPECTED  ( (nsel_CONFIG_SELECT_EXPECTED == nsel_EXPECTED_STD) || ((nsel_CONFIG_SELECT_EXPECTED == nsel_EXPECTED_DEFAULT) && nsel_HAVE_STD_EXPECTED) )
+
+//
+// in_place: code duplicated in any-lite, expected-lite, expected-lite, value-ptr-lite, variant-lite:
+//
+
+#ifndef future_std_lite_HAVE_IN_PLACE_TYPES
+#define future_std_lite_HAVE_IN_PLACE_TYPES  1
+
+// C++17 std::in_place in <utility>:
+
+#if nsel_CPP17_OR_GREATER
+
+#include <utility>
+
+namespace future_std {
+
+using std::in_place;
+using std::in_place_type;
+using std::in_place_index;
+using std::in_place_t;
+using std::in_place_type_t;
+using std::in_place_index_t;
+
+#define future_std_lite_in_place_t(      T)  std::in_place_t
+#define future_std_lite_in_place_type_t( T)  std::in_place_type_t<T>
+#define future_std_lite_in_place_index_t(K)  std::in_place_index_t<K>
+
+#define future_std_lite_in_place(      T)    std::in_place_t{}
+#define future_std_lite_in_place_type( T)    std::in_place_type_t<T>{}
+#define future_std_lite_in_place_index(K)    std::in_place_index_t<K>{}
+
+} // namespace future_std
+
+#else // nsel_CPP17_OR_GREATER
+
+#include <cstddef>
+
+namespace future_std {
+namespace detail {
+
+template< class T >
+struct in_place_type_tag {};
+
+template< std::size_t K >
+struct in_place_index_tag {};
+
+} // namespace detail
+
+struct in_place_t {};
+
+template< class T >
+inline in_place_t in_place( detail::in_place_type_tag<T> = detail::in_place_type_tag<T>() )
+{
+    return in_place_t();
+}
+
+template< std::size_t K >
+inline in_place_t in_place( detail::in_place_index_tag<K> = detail::in_place_index_tag<K>() )
+{
+    return in_place_t();
+}
+
+template< class T >
+inline in_place_t in_place_type( detail::in_place_type_tag<T> = detail::in_place_type_tag<T>() )
+{
+    return in_place_t();
+}
+
+template< std::size_t K >
+inline in_place_t in_place_index( detail::in_place_index_tag<K> = detail::in_place_index_tag<K>() )
+{
+    return in_place_t();
+}
+
+// mimic templated typedef:
+
+#define future_std_lite_in_place_t(      T)  future_std::in_place_t(&)( future_std::detail::in_place_type_tag<T>  )
+#define future_std_lite_in_place_type_t( T)  future_std::in_place_t(&)( future_std::detail::in_place_type_tag<T>  )
+#define future_std_lite_in_place_index_t(K)  future_std::in_place_t(&)( future_std::detail::in_place_index_tag<K> )
+
+#define future_std_lite_in_place(      T)    future_std::in_place_type<T>
+#define future_std_lite_in_place_type( T)    future_std::in_place_type<T>
+#define future_std_lite_in_place_index(K)    future_std::in_place_index<K>
+
+} // namespace future_std
+
+#endif // nsel_CPP17_OR_GREATER
+#endif // future_std_lite_HAVE_IN_PLACE_TYPES
+
+//
+// Using std::expected:
+//
+
+#if nsel_USES_STD_EXPECTED
+
+#include <expected>
+
+namespace future_std {
+
+    using std::expected;
+//  ...
+}
+
+#else // nsel_USES_STD_EXPECTED
+
+#include <cassert>
+#include <exception>
+#include <functional>
+#include <initializer_list>
+#include <memory>
+#include <new>
+#include <system_error>
+#include <type_traits>
+#include <utility>
+
+// additional includes:
+
+#if nsel_CONFIG_NO_EXCEPTIONS
+# if nsel_CONFIG_NO_EXCEPTIONS_SEH
+#  include <windows.h>   // for ExceptionCodes
+# else
+// already included: <cassert>
+# endif
+#else
+# include <stdexcept>
+#endif
+
+// C++ feature usage:
+
+#if nsel_CPP11_OR_GREATER
+# define nsel_constexpr  constexpr
+#else
+# define nsel_constexpr  /*constexpr*/
+#endif
+
+#if nsel_CPP14_OR_GREATER
+# define nsel_constexpr14 constexpr
+#else
+# define nsel_constexpr14 /*constexpr*/
+#endif
+
+#if nsel_CPP17_OR_GREATER
+# define nsel_inline17 inline
+#else
+# define nsel_inline17 /*inline*/
+#endif
+
+// Compiler versions:
+//
+// MSVC++  6.0  _MSC_VER == 1200  nsel_COMPILER_MSVC_VERSION ==  60  (Visual Studio 6.0)
+// MSVC++  7.0  _MSC_VER == 1300  nsel_COMPILER_MSVC_VERSION ==  70  (Visual Studio .NET 2002)
+// MSVC++  7.1  _MSC_VER == 1310  nsel_COMPILER_MSVC_VERSION ==  71  (Visual Studio .NET 2003)
+// MSVC++  8.0  _MSC_VER == 1400  nsel_COMPILER_MSVC_VERSION ==  80  (Visual Studio 2005)
+// MSVC++  9.0  _MSC_VER == 1500  nsel_COMPILER_MSVC_VERSION ==  90  (Visual Studio 2008)
+// MSVC++ 10.0  _MSC_VER == 1600  nsel_COMPILER_MSVC_VERSION == 100  (Visual Studio 2010)
+// MSVC++ 11.0  _MSC_VER == 1700  nsel_COMPILER_MSVC_VERSION == 110  (Visual Studio 2012)
+// MSVC++ 12.0  _MSC_VER == 1800  nsel_COMPILER_MSVC_VERSION == 120  (Visual Studio 2013)
+// MSVC++ 14.0  _MSC_VER == 1900  nsel_COMPILER_MSVC_VERSION == 140  (Visual Studio 2015)
+// MSVC++ 14.1  _MSC_VER >= 1910  nsel_COMPILER_MSVC_VERSION == 141  (Visual Studio 2017)
+// MSVC++ 14.2  _MSC_VER >= 1920  nsel_COMPILER_MSVC_VERSION == 142  (Visual Studio 2019)
+
+#if defined(_MSC_VER) && !defined(__clang__)
+# define nsel_COMPILER_MSVC_VER      (_MSC_VER )
+# define nsel_COMPILER_MSVC_VERSION  (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900)) )
+#else
+# define nsel_COMPILER_MSVC_VER      0
+# define nsel_COMPILER_MSVC_VERSION  0
+#endif
+
+#define nsel_COMPILER_VERSION( major, minor, patch )  ( 10 * ( 10 * (major) + (minor) ) + (patch) )
+
+#if defined(__clang__)
+# define nsel_COMPILER_CLANG_VERSION  nsel_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
+#else
+# define nsel_COMPILER_CLANG_VERSION  0
+#endif
+
+#if defined(__GNUC__) && !defined(__clang__)
+# define nsel_COMPILER_GNUC_VERSION  nsel_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
+#else
+# define nsel_COMPILER_GNUC_VERSION  0
+#endif
+
+// half-open range [lo..hi):
+//#define nsel_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) )
+
+// Method enabling
+
+#define nsel_REQUIRES_0(...) \
+    template< bool B = (__VA_ARGS__), typename std::enable_if<B, int>::type = 0 >
+
+#define nsel_REQUIRES_T(...) \
+    , typename std::enable_if< (__VA_ARGS__), int >::type = 0
+
+#define nsel_REQUIRES_R(R, ...) \
+    typename std::enable_if< (__VA_ARGS__), R>::type
+
+#define nsel_REQUIRES_A(...) \
+    , typename std::enable_if< (__VA_ARGS__), void*>::type = nullptr
+
+// Presence of language and library features:
+
+#ifdef _HAS_CPP0X
+# define nsel_HAS_CPP0X  _HAS_CPP0X
+#else
+# define nsel_HAS_CPP0X  0
+#endif
+
+//#define nsel_CPP11_140  (nsel_CPP11_OR_GREATER || nsel_COMPILER_MSVC_VER >= 1900)
+
+// Clang, GNUC, MSVC warning suppression macros:
+
+#ifdef __clang__
+# pragma clang diagnostic push
+#elif defined  __GNUC__
+# pragma  GCC  diagnostic push
+#endif // __clang__
+
+#if nsel_COMPILER_MSVC_VERSION >= 140
+# pragma warning( push )
+# define nsel_DISABLE_MSVC_WARNINGS(codes)  __pragma( warning(disable: codes) )
+#else
+# define nsel_DISABLE_MSVC_WARNINGS(codes)
+#endif
+
+#ifdef __clang__
+# define nsel_RESTORE_WARNINGS()  _Pragma("clang diagnostic pop")
+#elif defined __GNUC__
+# define nsel_RESTORE_WARNINGS()  _Pragma("GCC diagnostic pop")
+#elif nsel_COMPILER_MSVC_VERSION >= 140
+# define nsel_RESTORE_WARNINGS()  __pragma( warning( pop ) )
+#else
+# define nsel_RESTORE_WARNINGS()
+#endif
+
+// Suppress the following MSVC (GSL) warnings:
+// - C26409: Avoid calling new and delete explicitly, use std::make_unique<T> instead (r.11)
+
+nsel_DISABLE_MSVC_WARNINGS( 26409 )
+
+//
+// expected:
+//
+
+namespace future_std { namespace expected_lite {
+
+// type traits C++17:
+
+namespace std17 {
+
+#if nsel_CPP17_OR_GREATER
+
+using std::conjunction;
+using std::is_swappable;
+using std::is_nothrow_swappable;
+
+#else // nsel_CPP17_OR_GREATER
+
+namespace detail {
+
+using std::swap;
+
+struct is_swappable
+{
+    template< typename T, typename = decltype( swap( std::declval<T&>(), std::declval<T&>() ) ) >
+    static std::true_type test( int /* unused */);
+
+    template< typename >
+    static std::false_type test(...);
+};
+
+struct is_nothrow_swappable
+{
+    // wrap noexcept(expr) in separate function as work-around for VC140 (VS2015):
+
+    template< typename T >
+    static constexpr bool satisfies()
+    {
+        return noexcept( swap( std::declval<T&>(), std::declval<T&>() ) );
+    }
+
+    template< typename T >
+    static auto test( int ) -> std::integral_constant<bool, satisfies<T>()>{}
+
+    template< typename >
+    static auto test(...) -> std::false_type;
+};
+} // namespace detail
+
+// is [nothrow] swappable:
+
+template< typename T >
+struct is_swappable : decltype( detail::is_swappable::test<T>(0) ){};
+
+template< typename T >
+struct is_nothrow_swappable : decltype( detail::is_nothrow_swappable::test<T>(0) ){};
+
+// conjunction:
+
+template< typename... > struct conjunction : std::true_type{};
+template< typename B1 > struct conjunction<B1> : B1{};
+
+template< typename B1, typename... Bn >
+struct conjunction<B1, Bn...> : std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type{};
+
+#endif // nsel_CPP17_OR_GREATER
+
+} // namespace std17
+
+// type traits C++20:
+
+namespace std20 {
+
+#if defined(__cpp_lib_remove_cvref)
+
+using std::remove_cvref;
+
+#else
+
+template< typename T >
+struct remove_cvref
+{
+    typedef typename std::remove_cv< typename std::remove_reference<T>::type >::type type;
+};
+
+#endif
+
+} // namespace std20
+
+// forward declaration:
+
+template< typename T, typename E >
+class expected;
+
+namespace detail {
+
+#if nsel_P2505R >= 3
+template< typename T >
+struct is_expected : std::false_type {};
+
+template< typename T, typename E >
+struct is_expected< expected< T, E > > : std::true_type {};
+#endif // nsel_P2505R >= 3
+
+/// discriminated union to hold value or 'error'.
+
+template< typename T, typename E >
+class storage_t_noncopy_nonmove_impl
+{
+    template< typename, typename > friend class future_std::expected_lite::expected;
+
+public:
+    using value_type = T;
+    using error_type = E;
+
+    // no-op construction
+    storage_t_noncopy_nonmove_impl() {}
+    ~storage_t_noncopy_nonmove_impl() {}
+
+    explicit storage_t_noncopy_nonmove_impl( bool has_value )
+        : m_has_value( has_value )
+    {}
+
+    void construct_value()
+    {
+        new( &m_value ) value_type();
+    }
+
+    // void construct_value( value_type const & e )
+    // {
+    //     new( &m_value ) value_type( e );
+    // }
+
+    // void construct_value( value_type && e )
+    // {
+    //     new( &m_value ) value_type( std::move( e ) );
+    // }
+
+    template< class... Args >
+    void emplace_value( Args&&... args )
+    {
+        new( &m_value ) value_type( std::forward<Args>(args)...);
+    }
+
+    template< class U, class... Args >
+    void emplace_value( std::initializer_list<U> il, Args&&... args )
+    {
+        new( &m_value ) value_type( il, std::forward<Args>(args)... );
+    }
+
+    void destruct_value()
+    {
+        m_value.~value_type();
+    }
+
+    // void construct_error( error_type const & e )
+    // {
+    //     // new( &m_error ) error_type( e );
+    // }
+
+    // void construct_error( error_type && e )
+    // {
+    //     // new( &m_error ) error_type( std::move( e ) );
+    // }
+
+    template< class... Args >
+    void emplace_error( Args&&... args )
+    {
+        new( &m_error ) error_type( std::forward<Args>(args)...);
+    }
+
+    template< class U, class... Args >
+    void emplace_error( std::initializer_list<U> il, Args&&... args )
+    {
+        new( &m_error ) error_type( il, std::forward<Args>(args)... );
+    }
+
+    void destruct_error()
+    {
+        m_error.~error_type();
+    }
+
+    constexpr value_type const & value() const &
+    {
+        return m_value;
+    }
+
+    value_type & value() &
+    {
+        return m_value;
+    }
+
+    constexpr value_type const && value() const &&
+    {
+        return std::move( m_value );
+    }
+
+    nsel_constexpr14 value_type && value() &&
+    {
+        return std::move( m_value );
+    }
+
+    value_type const * value_ptr() const
+    {
+        return &m_value;
+    }
+
+    value_type * value_ptr()
+    {
+        return &m_value;
+    }
+
+    error_type const & error() const &
+    {
+        return m_error;
+    }
+
+    error_type & error() &
+    {
+        return m_error;
+    }
+
+    constexpr error_type const && error() const &&
+    {
+        return std::move( m_error );
+    }
+
+    nsel_constexpr14 error_type && error() &&
+    {
+        return std::move( m_error );
+    }
+
+    bool has_value() const
+    {
+        return m_has_value;
+    }
+
+    void set_has_value( bool v )
+    {
+        m_has_value = v;
+    }
+
+private:
+    union
+    {
+        value_type m_value;
+        error_type m_error;
+    };
+
+    bool m_has_value = false;
+};
+
+template< typename T, typename E >
+class storage_t_impl
+{
+    template< typename, typename > friend class future_std::expected_lite::expected;
+
+public:
+    using value_type = T;
+    using error_type = E;
+
+    // no-op construction
+    storage_t_impl() {}
+    ~storage_t_impl() {}
+
+    explicit storage_t_impl( bool has_value )
+        : m_has_value( has_value )
+    {}
+
+    void construct_value()
+    {
+        new( &m_value ) value_type();
+    }
+
+    void construct_value( value_type const & e )
+    {
+        new( &m_value ) value_type( e );
+    }
+
+    void construct_value( value_type && e )
+    {
+        new( &m_value ) value_type( std::move( e ) );
+    }
+
+    template< class... Args >
+    void emplace_value( Args&&... args )
+    {
+        new( &m_value ) value_type( std::forward<Args>(args)...);
+    }
+
+    template< class U, class... Args >
+    void emplace_value( std::initializer_list<U> il, Args&&... args )
+    {
+        new( &m_value ) value_type( il, std::forward<Args>(args)... );
+    }
+
+    void destruct_value()
+    {
+        m_value.~value_type();
+    }
+
+    void construct_error( error_type const & e )
+    {
+        new( &m_error ) error_type( e );
+    }
+
+    void construct_error( error_type && e )
+    {
+        new( &m_error ) error_type( std::move( e ) );
+    }
+
+    template< class... Args >
+    void emplace_error( Args&&... args )
+    {
+        new( &m_error ) error_type( std::forward<Args>(args)...);
+    }
+
+    template< class U, class... Args >
+    void emplace_error( std::initializer_list<U> il, Args&&... args )
+    {
+        new( &m_error ) error_type( il, std::forward<Args>(args)... );
+    }
+
+    void destruct_error()
+    {
+        m_error.~error_type();
+    }
+
+    constexpr value_type const & value() const &
+    {
+        return m_value;
+    }
+
+    value_type & value() &
+    {
+        return m_value;
+    }
+
+    constexpr value_type const && value() const &&
+    {
+        return std::move( m_value );
+    }
+
+    nsel_constexpr14 value_type && value() &&
+    {
+        return std::move( m_value );
+    }
+
+    value_type const * value_ptr() const
+    {
+        return &m_value;
+    }
+
+    value_type * value_ptr()
+    {
+        return &m_value;
+    }
+
+    error_type const & error() const &
+    {
+        return m_error;
+    }
+
+    error_type & error() &
+    {
+        return m_error;
+    }
+
+    constexpr error_type const && error() const &&
+    {
+        return std::move( m_error );
+    }
+
+    nsel_constexpr14 error_type && error() &&
+    {
+        return std::move( m_error );
+    }
+
+    bool has_value() const
+    {
+        return m_has_value;
+    }
+
+    void set_has_value( bool v )
+    {
+        m_has_value = v;
+    }
+
+private:
+    union
+    {
+        value_type m_value;
+        error_type m_error;
+    };
+
+    bool m_has_value = false;
+};
+
+/// discriminated union to hold only 'error'.
+
+template< typename E >
+struct storage_t_impl<void, E>
+{
+    template< typename, typename > friend class future_std::expected_lite::expected;
+
+public:
+    using value_type = void;
+    using error_type = E;
+
+    // no-op construction
+    storage_t_impl() {}
+    ~storage_t_impl() {}
+
+    explicit storage_t_impl( bool has_value )
+        : m_has_value( has_value )
+    {}
+
+    void construct_error( error_type const & e )
+    {
+        new( &m_error ) error_type( e );
+    }
+
+    void construct_error( error_type && e )
+    {
+        new( &m_error ) error_type( std::move( e ) );
+    }
+
+    template< class... Args >
+    void emplace_error( Args&&... args )
+    {
+        new( &m_error ) error_type( std::forward<Args>(args)...);
+    }
+
+    template< class U, class... Args >
+    void emplace_error( std::initializer_list<U> il, Args&&... args )
+    {
+        new( &m_error ) error_type( il, std::forward<Args>(args)... );
+    }
+
+    void destruct_error()
+    {
+        m_error.~error_type();
+    }
+
+    error_type const & error() const &
+    {
+        return m_error;
+    }
+
+    error_type & error() &
+    {
+        return m_error;
+    }
+
+    constexpr error_type const && error() const &&
+    {
+        return std::move( m_error );
+    }
+
+    nsel_constexpr14 error_type && error() &&
+    {
+        return std::move( m_error );
+    }
+
+    bool has_value() const
+    {
+        return m_has_value;
+    }
+
+    void set_has_value( bool v )
+    {
+        m_has_value = v;
+    }
+
+private:
+    union
+    {
+        char m_dummy;
+        error_type m_error;
+    };
+
+    bool m_has_value = false;
+};
+
+template< typename T, typename E, bool isConstructable, bool isMoveable >
+class storage_t
+{
+public:
+};
+
+template< typename T, typename E >
+class storage_t<T, E, false, false> : public storage_t_noncopy_nonmove_impl<T, E>
+{
+public:
+    storage_t() = default;
+    ~storage_t() = default;
+
+    explicit storage_t( bool has_value )
+        : storage_t_noncopy_nonmove_impl<T, E>( has_value )
+    {}
+
+    storage_t( storage_t const & other ) = delete;
+    storage_t( storage_t &&      other ) = delete;
+
+};
+
+template< typename T, typename E >
+class storage_t<T, E, true, true> : public storage_t_impl<T, E>
+{
+public:
+    storage_t() = default;
+    ~storage_t() = default;
+
+    explicit storage_t( bool has_value )
+        : storage_t_impl<T, E>( has_value )
+    {}
+
+    storage_t( storage_t const & other )
+        : storage_t_impl<T, E>( other.has_value() )
+    {
+        if ( this->has_value() ) this->construct_value( other.value() );
+        else                     this->construct_error( other.error() );
+    }
+
+    storage_t(storage_t && other )
+        : storage_t_impl<T, E>( other.has_value() )
+    {
+        if ( this->has_value() ) this->construct_value( std::move( other.value() ) );
+        else                     this->construct_error( std::move( other.error() ) );
+    }
+};
+
+template< typename E >
+class storage_t<void, E, true, true> : public storage_t_impl<void, E>
+{
+public:
+    storage_t() = default;
+    ~storage_t() = default;
+
+    explicit storage_t( bool has_value )
+        : storage_t_impl<void, E>( has_value )
+    {}
+
+    storage_t( storage_t const & other )
+        : storage_t_impl<void, E>( other.has_value() )
+    {
+        if ( this->has_value() ) ;
+        else                     this->construct_error( other.error() );
+    }
+
+    storage_t(storage_t && other )
+        : storage_t_impl<void, E>( other.has_value() )
+    {
+        if ( this->has_value() ) ;
+        else                     this->construct_error( std::move( other.error() ) );
+    }
+};
+
+template< typename T, typename E >
+class storage_t<T, E, true, false> : public storage_t_impl<T, E>
+{
+public:
+    storage_t() = default;
+    ~storage_t() = default;
+
+    explicit storage_t( bool has_value )
+        : storage_t_impl<T, E>( has_value )
+    {}
+
+    storage_t( storage_t const & other )
+        : storage_t_impl<T, E>(other.has_value())
+    {
+        if ( this->has_value() ) this->construct_value( other.value() );
+        else                     this->construct_error( other.error() );
+    }
+
+    storage_t( storage_t && other ) = delete;
+};
+
+template< typename E >
+class storage_t<void, E, true, false> : public storage_t_impl<void, E>
+{
+public:
+    storage_t() = default;
+    ~storage_t() = default;
+
+    explicit storage_t( bool has_value )
+        : storage_t_impl<void, E>( has_value )
+    {}
+
+    storage_t( storage_t const & other )
+        : storage_t_impl<void, E>(other.has_value())
+    {
+        if ( this->has_value() ) ;
+        else                     this->construct_error( other.error() );
+    }
+
+    storage_t( storage_t && other ) = delete;
+};
+
+template< typename T, typename E >
+class storage_t<T, E, false, true> : public storage_t_impl<T, E>
+{
+public:
+    storage_t() = default;
+    ~storage_t() = default;
+
+    explicit storage_t( bool has_value )
+        : storage_t_impl<T, E>( has_value )
+    {}
+
+    storage_t( storage_t const & other ) = delete;
+
+    storage_t( storage_t && other )
+        : storage_t_impl<T, E>( other.has_value() )
+    {
+        if ( this->has_value() ) this->construct_value( std::move( other.value() ) );
+        else                     this->construct_error( std::move( other.error() ) );
+    }
+};
+
+template< typename E >
+class storage_t<void, E, false, true> : public storage_t_impl<void, E>
+{
+public:
+    storage_t() = default;
+    ~storage_t() = default;
+
+    explicit storage_t( bool has_value )
+        : storage_t_impl<void, E>( has_value )
+    {}
+
+    storage_t( storage_t const & other ) = delete;
+
+    storage_t( storage_t && other )
+        : storage_t_impl<void, E>( other.has_value() )
+    {
+        if ( this->has_value() ) ;
+        else                     this->construct_error( std::move( other.error() ) );
+    }
+};
+
+#if nsel_P2505R >= 3
+// C++11 invoke implementation
+template< typename >
+struct is_reference_wrapper : std::false_type {};
+template< typename T >
+struct is_reference_wrapper< std::reference_wrapper< T > > : std::true_type {};
+
+template< typename FnT, typename ClassT, typename ObjectT, typename... Args
+    nsel_REQUIRES_T(
+        std::is_function<FnT>::value
+        && ( std::is_same< ClassT, typename std20::remove_cvref< ObjectT >::type >::value
+        || std::is_base_of< ClassT, typename std20::remove_cvref< ObjectT >::type >::value )
+    )
+>
+nsel_constexpr auto invoke_member_function_impl( FnT ClassT::* memfnptr, ObjectT && obj, Args && ... args )
+        noexcept( noexcept( (std::forward< ObjectT >( obj ).*memfnptr)( std::forward< Args >( args )... ) ) )
+        -> decltype( (std::forward< ObjectT >( obj ).*memfnptr)( std::forward< Args >( args )...) )
+{
+      return (std::forward< ObjectT >( obj ).*memfnptr)( std::forward< Args >( args )... );
+}
+
+template< typename FnT, typename ClassT, typename ObjectT, typename... Args
+    nsel_REQUIRES_T(
+        std::is_function<FnT>::value
+        && is_reference_wrapper< typename std20::remove_cvref< ObjectT >::type >::value
+    )
+>
+nsel_constexpr auto invoke_member_function_impl( FnT ClassT::* memfnptr, ObjectT && obj, Args && ... args )
+        noexcept( noexcept( (obj.get().*memfnptr)( std::forward< Args >( args ) ... ) ) )
+        -> decltype( (obj.get().*memfnptr)( std::forward< Args >( args ) ... ) )
+{
+    return (obj.get().*memfnptr)( std::forward< Args >( args ) ... );
+}
+
+template< typename FnT, typename ClassT, typename ObjectT, typename... Args
+    nsel_REQUIRES_T(
+        std::is_function<FnT>::value
+        && !std::is_same< ClassT, typename std20::remove_cvref< ObjectT >::type >::value
+        && !std::is_base_of< ClassT, typename std20::remove_cvref< ObjectT >::type >::value
+        && !is_reference_wrapper< typename std20::remove_cvref< ObjectT >::type >::value
+    )
+>
+nsel_constexpr auto invoke_member_function_impl( FnT ClassT::* memfnptr, ObjectT && obj, Args && ... args )
+        noexcept( noexcept( ((*std::forward< ObjectT >( obj )).*memfnptr)( std::forward< Args >( args ) ... ) ) )
+        -> decltype( ((*std::forward< ObjectT >( obj )).*memfnptr)( std::forward< Args >( args ) ... ) )
+{
+    return ((*std::forward<ObjectT>(obj)).*memfnptr)( std::forward< Args >( args ) ... );
+}
+
+template< typename MemberT, typename ClassT, typename ObjectT
+    nsel_REQUIRES_T(
+        std::is_same< ClassT, typename std20::remove_cvref< ObjectT >::type >::value
+        || std::is_base_of< ClassT, typename std20::remove_cvref< ObjectT >::type >::value
+    )
+>
+nsel_constexpr auto invoke_member_object_impl( MemberT ClassT::* memobjptr, ObjectT && obj )
+        noexcept( noexcept( std::forward< ObjectT >( obj ).*memobjptr ) )
+        -> decltype( std::forward< ObjectT >( obj ).*memobjptr )
+{
+    return std::forward< ObjectT >( obj ).*memobjptr;
+}
+
+template< typename MemberT, typename ClassT, typename ObjectT
+    nsel_REQUIRES_T(
+        is_reference_wrapper< typename std20::remove_cvref< ObjectT >::type >::value
+    )
+>
+nsel_constexpr auto invoke_member_object_impl( MemberT ClassT::* memobjptr, ObjectT && obj )
+        noexcept( noexcept( obj.get().*memobjptr ) )
+        -> decltype( obj.get().*memobjptr )
+{
+    return obj.get().*memobjptr;
+}
+
+template< typename MemberT, typename ClassT, typename ObjectT
+    nsel_REQUIRES_T(
+        !std::is_same< ClassT, typename std20::remove_cvref< ObjectT >::type >::value
+        && !std::is_base_of< ClassT, typename std20::remove_cvref< ObjectT >::type >::value
+        && !is_reference_wrapper< typename std20::remove_cvref< ObjectT >::type >::value
+    )
+>
+nsel_constexpr auto invoke_member_object_impl( MemberT ClassT::* memobjptr, ObjectT && obj )
+        noexcept( noexcept( (*std::forward< ObjectT >( obj )).*memobjptr ) )
+        -> decltype( (*std::forward< ObjectT >( obj )).*memobjptr )
+{
+    return (*std::forward< ObjectT >( obj )).*memobjptr;
+}
+
+template< typename F, typename... Args
+    nsel_REQUIRES_T(
+        std::is_member_function_pointer< typename std20::remove_cvref< F >::type >::value
+    )
+>
+nsel_constexpr auto invoke( F && f, Args && ... args )
+        noexcept( noexcept( invoke_member_function_impl( std::forward< F >( f ), std::forward< Args >( args ) ... ) ) )
+        -> decltype( invoke_member_function_impl( std::forward< F >( f ), std::forward< Args >( args ) ... ) )
+{
+    return invoke_member_function_impl( std::forward< F >( f ), std::forward< Args >( args ) ... );
+}
+
+template< typename F, typename... Args
+    nsel_REQUIRES_T(
+        std::is_member_object_pointer< typename std20::remove_cvref< F >::type >::value
+    )
+>
+nsel_constexpr auto invoke( F && f, Args && ... args )
+        noexcept( noexcept( invoke_member_object_impl( std::forward< F >( f ), std::forward< Args >( args ) ... ) ) )
+        -> decltype( invoke_member_object_impl( std::forward< F >( f ), std::forward< Args >( args ) ... ) )
+{
+    return invoke_member_object_impl( std::forward< F >( f ), std::forward< Args >( args ) ... );
+}
+
+template< typename F, typename... Args
+    nsel_REQUIRES_T(
+        !std::is_member_function_pointer< typename std20::remove_cvref< F >::type >::value
+        && !std::is_member_object_pointer< typename std20::remove_cvref< F >::type >::value
+    )
+>
+nsel_constexpr auto invoke( F && f, Args && ... args )
+        noexcept( noexcept( std::forward< F >( f )( std::forward< Args >( args ) ... ) ) )
+        -> decltype( std::forward< F >( f )( std::forward< Args >( args ) ... ) )
+{
+    return std::forward< F >( f )( std::forward< Args >( args ) ... );
+}
+
+template< typename F, typename ... Args >
+using invoke_result_nocvref_t = typename std20::remove_cvref< decltype( invoke( std::declval< F >(), std::declval< Args >()... ) ) >::type;
+
+#if nsel_P2505R >= 5
+template< typename F, typename ... Args >
+using transform_invoke_result_t = typename std::remove_cv< decltype( invoke( std::declval< F >(), std::declval< Args >()... ) ) >::type;
+#else
+template< typename F, typename ... Args >
+using transform_invoke_result_t = invoke_result_nocvref_t
+#endif // nsel_P2505R >= 5
+
+template< typename T >
+struct valid_expected_value_type : std::integral_constant< bool, std::is_destructible< T >::value && !std::is_reference< T >::value && !std::is_array< T >::value > {};
+
+#endif // nsel_P2505R >= 3
+} // namespace detail
+
+/// x.x.5 Unexpected object type; unexpected_type; C++17 and later can also use aliased type unexpected.
+
+#if nsel_P0323R <= 2
+template< typename E = std::exception_ptr >
+class unexpected_type
+#else
+template< typename E >
+class unexpected_type
+#endif // nsel_P0323R
+{
+public:
+    using error_type = E;
+
+    // x.x.5.2.1 Constructors
+
+//  unexpected_type() = delete;
+
+    constexpr unexpected_type( unexpected_type const & ) = default;
+    constexpr unexpected_type( unexpected_type && ) = default;
+
+    template< typename... Args
+        nsel_REQUIRES_T(
+            std::is_constructible<E, Args&&...>::value
+        )
+    >
+    constexpr explicit unexpected_type( future_std_lite_in_place_t(E), Args &&... args )
+    : m_error( std::forward<Args>( args )...)
+    {}
+
+    template< typename U, typename... Args
+        nsel_REQUIRES_T(
+            std::is_constructible<E, std::initializer_list<U>, Args&&...>::value
+        )
+    >
+    constexpr explicit unexpected_type( future_std_lite_in_place_t(E), std::initializer_list<U> il, Args &&... args )
+    : m_error( il, std::forward<Args>( args )...)
+    {}
+
+    template< typename E2
+        nsel_REQUIRES_T(
+            std::is_constructible<E,E2>::value
+            && !std::is_same< typename std20::remove_cvref<E2>::type, future_std_lite_in_place_t(E2) >::value
+            && !std::is_same< typename std20::remove_cvref<E2>::type, unexpected_type >::value
+        )
+    >
+    constexpr explicit unexpected_type( E2 && error )
+    : m_error( std::forward<E2>( error ) )
+    {}
+
+    template< typename E2
+        nsel_REQUIRES_T(
+            std::is_constructible<    E, E2>::value
+            && !std::is_constructible<E, unexpected_type<E2>       &   >::value
+            && !std::is_constructible<E, unexpected_type<E2>           >::value
+            && !std::is_constructible<E, unexpected_type<E2> const &   >::value
+            && !std::is_constructible<E, unexpected_type<E2> const     >::value
+            && !std::is_convertible<     unexpected_type<E2>       &, E>::value
+            && !std::is_convertible<     unexpected_type<E2>        , E>::value
+            && !std::is_convertible<     unexpected_type<E2> const &, E>::value
+            && !std::is_convertible<     unexpected_type<E2> const  , E>::value
+            && !std::is_convertible< E2 const &, E>::value /*=> explicit */
+        )
+    >
+    constexpr explicit unexpected_type( unexpected_type<E2> const & error )
+    : m_error( E{ error.value() } )
+    {}
+
+    template< typename E2
+        nsel_REQUIRES_T(
+            std::is_constructible<    E, E2>::value
+            && !std::is_constructible<E, unexpected_type<E2>       &   >::value
+            && !std::is_constructible<E, unexpected_type<E2>           >::value
+            && !std::is_constructible<E, unexpected_type<E2> const &   >::value
+            && !std::is_constructible<E, unexpected_type<E2> const     >::value
+            && !std::is_convertible<     unexpected_type<E2>       &, E>::value
+            && !std::is_convertible<     unexpected_type<E2>        , E>::value
+            && !std::is_convertible<     unexpected_type<E2> const &, E>::value
+            && !std::is_convertible<     unexpected_type<E2> const  , E>::value
+            &&  std::is_convertible< E2 const &, E>::value /*=> explicit */
+        )
+    >
+    constexpr /*non-explicit*/ unexpected_type( unexpected_type<E2> const & error )
+    : m_error( error.value() )
+    {}
+
+    template< typename E2
+        nsel_REQUIRES_T(
+            std::is_constructible<    E, E2>::value
+            && !std::is_constructible<E, unexpected_type<E2>       &   >::value
+            && !std::is_constructible<E, unexpected_type<E2>           >::value
+            && !std::is_constructible<E, unexpected_type<E2> const &   >::value
+            && !std::is_constructible<E, unexpected_type<E2> const     >::value
+            && !std::is_convertible<     unexpected_type<E2>       &, E>::value
+            && !std::is_convertible<     unexpected_type<E2>        , E>::value
+            && !std::is_convertible<     unexpected_type<E2> const &, E>::value
+            && !std::is_convertible<     unexpected_type<E2> const  , E>::value
+            && !std::is_convertible< E2 const &, E>::value /*=> explicit */
+        )
+    >
+    constexpr explicit unexpected_type( unexpected_type<E2> && error )
+    : m_error( E{ std::move( error.value() ) } )
+    {}
+
+    template< typename E2
+        nsel_REQUIRES_T(
+            std::is_constructible<    E, E2>::value
+            && !std::is_constructible<E, unexpected_type<E2>       &   >::value
+            && !std::is_constructible<E, unexpected_type<E2>           >::value
+            && !std::is_constructible<E, unexpected_type<E2> const &   >::value
+            && !std::is_constructible<E, unexpected_type<E2> const     >::value
+            && !std::is_convertible<     unexpected_type<E2>       &, E>::value
+            && !std::is_convertible<     unexpected_type<E2>        , E>::value
+            && !std::is_convertible<     unexpected_type<E2> const &, E>::value
+            && !std::is_convertible<     unexpected_type<E2> const  , E>::value
+            &&  std::is_convertible< E2 const &, E>::value /*=> non-explicit */
+        )
+    >
+    constexpr /*non-explicit*/ unexpected_type( unexpected_type<E2> && error )
+    : m_error( std::move( error.value() ) )
+    {}
+
+    // x.x.5.2.2 Assignment
+
+    nsel_constexpr14 unexpected_type& operator=( unexpected_type const & ) = default;
+    nsel_constexpr14 unexpected_type& operator=( unexpected_type && ) = default;
+
+    template< typename E2 = E >
+    nsel_constexpr14 unexpected_type & operator=( unexpected_type<E2> const & other )
+    {
+        unexpected_type{ other.value() }.swap( *this );
+        return *this;
+    }
+
+    template< typename E2 = E >
+    nsel_constexpr14 unexpected_type & operator=( unexpected_type<E2> && other )
+    {
+        unexpected_type{ std::move( other.value() ) }.swap( *this );
+        return *this;
+    }
+
+    // x.x.5.2.3 Observers
+
+    nsel_constexpr14 E & value() & noexcept
+    {
+        return m_error;
+    }
+
+    constexpr E const & value() const & noexcept
+    {
+        return m_error;
+    }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+
+    nsel_constexpr14 E && value() && noexcept
+    {
+        return std::move( m_error );
+    }
+
+    constexpr E const && value() const && noexcept
+    {
+        return std::move( m_error );
+    }
+
+#endif
+
+    // x.x.5.2.4 Swap
+
+    template< typename U=E >
+    nsel_REQUIRES_R( void,
+        std17::is_swappable<U>::value
+    )
+    swap( unexpected_type & other ) noexcept (
+        std17::is_nothrow_swappable<U>::value
+    )
+    {
+        using std::swap;
+        swap( m_error, other.m_error );
+    }
+
+    // TODO: ??? unexpected_type: in-class friend operator==, !=
+
+private:
+    error_type m_error;
+};
+
+#if nsel_CPP17_OR_GREATER
+
+/// template deduction guide:
+
+template< typename E >
+unexpected_type( E ) -> unexpected_type< E >;
+
+#endif
+
+/// class unexpected_type, std::exception_ptr specialization (P0323R2)
+
+#if !nsel_CONFIG_NO_EXCEPTIONS
+#if  nsel_P0323R <= 2
+
+// TODO: Should expected be specialized for particular E types such as exception_ptr and how?
+//       See p0323r7 2.1. Ergonomics, http://wg21.link/p0323
+template<>
+class unexpected_type< std::exception_ptr >
+{
+public:
+    using error_type = std::exception_ptr;
+
+    unexpected_type() = delete;
+
+    ~unexpected_type(){}
+
+    explicit unexpected_type( std::exception_ptr const & error )
+    : m_error( error )
+    {}
+
+    explicit unexpected_type(std::exception_ptr && error )
+    : m_error( std::move( error ) )
+    {}
+
+    template< typename E >
+    explicit unexpected_type( E error )
+    : m_error( std::make_exception_ptr( error ) )
+    {}
+
+    std::exception_ptr const & value() const
+    {
+        return m_error;
+    }
+
+    std::exception_ptr & value()
+    {
+        return m_error;
+    }
+
+private:
+    std::exception_ptr m_error;
+};
+
+#endif // nsel_P0323R
+#endif // !nsel_CONFIG_NO_EXCEPTIONS
+
+/// x.x.4, Unexpected equality operators
+
+template< typename E1, typename E2 >
+constexpr bool operator==( unexpected_type<E1> const & x, unexpected_type<E2> const & y )
+{
+    return x.value() == y.value();
+}
+
+template< typename E1, typename E2 >
+constexpr bool operator!=( unexpected_type<E1> const & x, unexpected_type<E2> const & y )
+{
+    return ! ( x == y );
+}
+
+#if nsel_P0323R <= 2
+
+template< typename E >
+constexpr bool operator<( unexpected_type<E> const & x, unexpected_type<E> const & y )
+{
+    return x.value() < y.value();
+}
+
+template< typename E >
+constexpr bool operator>( unexpected_type<E> const & x, unexpected_type<E> const & y )
+{
+    return ( y < x );
+}
+
+template< typename E >
+constexpr bool operator<=( unexpected_type<E> const & x, unexpected_type<E> const & y )
+{
+    return ! ( y < x  );
+}
+
+template< typename E >
+constexpr bool operator>=( unexpected_type<E> const & x, unexpected_type<E> const & y )
+{
+    return ! ( x < y );
+}
+
+#endif // nsel_P0323R
+
+/// x.x.5 Specialized algorithms
+
+template< typename E
+    nsel_REQUIRES_T(
+        std17::is_swappable<E>::value
+    )
+>
+void swap( unexpected_type<E> & x, unexpected_type<E> & y) noexcept ( noexcept ( x.swap(y) ) )
+{
+    x.swap( y );
+}
+
+#if nsel_P0323R <= 2
+
+// unexpected: relational operators for std::exception_ptr:
+
+inline constexpr bool operator<( unexpected_type<std::exception_ptr> const & /*x*/, unexpected_type<std::exception_ptr> const & /*y*/ )
+{
+    return false;
+}
+
+inline constexpr bool operator>( unexpected_type<std::exception_ptr> const & /*x*/, unexpected_type<std::exception_ptr> const & /*y*/ )
+{
+    return false;
+}
+
+inline constexpr bool operator<=( unexpected_type<std::exception_ptr> const & x, unexpected_type<std::exception_ptr> const & y )
+{
+    return ( x == y );
+}
+
+inline constexpr bool operator>=( unexpected_type<std::exception_ptr> const & x, unexpected_type<std::exception_ptr> const & y )
+{
+    return ( x == y );
+}
+
+#endif // nsel_P0323R
+
+// unexpected: traits
+
+#if nsel_P0323R <= 3
+
+template< typename E>
+struct is_unexpected : std::false_type {};
+
+template< typename E>
+struct is_unexpected< unexpected_type<E> > : std::true_type {};
+
+#endif // nsel_P0323R
+
+// unexpected: factory
+
+// keep make_unexpected() removed in p0323r2 for pre-C++17:
+
+template< typename E>
+nsel_constexpr14 auto
+make_unexpected( E && value ) -> unexpected_type< typename std::decay<E>::type >
+{
+    return unexpected_type< typename std::decay<E>::type >( std::forward<E>(value) );
+}
+
+#if nsel_P0323R <= 3
+
+/*nsel_constexpr14*/ auto inline
+make_unexpected_from_current_exception() -> unexpected_type< std::exception_ptr >
+{
+    return unexpected_type< std::exception_ptr >( std::current_exception() );
+}
+
+#endif // nsel_P0323R
+
+/// x.x.6, x.x.7 expected access error
+
+template< typename E >
+class bad_expected_access;
+
+/// x.x.7 bad_expected_access<void>: expected access error
+
+template <>
+class bad_expected_access< void > : public std::exception
+{
+public:
+    explicit bad_expected_access()
+    : std::exception()
+    {}
+};
+
+/// x.x.6 bad_expected_access: expected access error
+
+#if !nsel_CONFIG_NO_EXCEPTIONS
+
+template< typename E >
+class bad_expected_access : public bad_expected_access< void >
+{
+public:
+    using error_type = E;
+
+    explicit bad_expected_access( error_type error )
+    : m_error( error )
+    {}
+
+    virtual char const * what() const noexcept override
+    {
+        return "bad_expected_access";
+    }
+
+    nsel_constexpr14 error_type & error() &
+    {
+        return m_error;
+    }
+
+    constexpr error_type const & error() const &
+    {
+        return m_error;
+    }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+
+    nsel_constexpr14 error_type && error() &&
+    {
+        return std::move( m_error );
+    }
+
+    constexpr error_type const && error() const &&
+    {
+        return std::move( m_error );
+    }
+
+#endif
+
+private:
+    error_type m_error;
+};
+
+#endif // nsel_CONFIG_NO_EXCEPTIONS
+
+/// x.x.8 unexpect tag, in_place_unexpected tag: construct an error
+
+struct unexpect_t{};
+using in_place_unexpected_t = unexpect_t;
+
+nsel_inline17 constexpr unexpect_t unexpect{};
+nsel_inline17 constexpr unexpect_t in_place_unexpected{};
+
+/// class error_traits
+
+#if nsel_CONFIG_NO_EXCEPTIONS
+
+namespace detail {
+    inline bool text( char const * /*text*/ ) { return true; }
+}
+
+template< typename Error >
+struct error_traits
+{
+    static void rethrow( Error const & /*e*/ )
+    {
+#if nsel_CONFIG_NO_EXCEPTIONS_SEH
+        RaiseException( EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 0, NULL );
+#else
+        assert( false && detail::text("throw bad_expected_access<Error>{ e };") );
+#endif
+    }
+};
+
+template<>
+struct error_traits< std::exception_ptr >
+{
+    static void rethrow( std::exception_ptr const & /*e*/ )
+    {
+#if nsel_CONFIG_NO_EXCEPTIONS_SEH
+        RaiseException( EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 0, NULL );
+#else
+        assert( false && detail::text("throw bad_expected_access<std::exception_ptr>{ e };") );
+#endif
+    }
+};
+
+template<>
+struct error_traits< std::error_code >
+{
+    static void rethrow( std::error_code const & /*e*/ )
+    {
+#if nsel_CONFIG_NO_EXCEPTIONS_SEH
+        RaiseException( EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 0, NULL );
+#else
+        assert( false && detail::text("throw std::system_error( e );") );
+#endif
+    }
+};
+
+#else // nsel_CONFIG_NO_EXCEPTIONS
+
+template< typename Error >
+struct error_traits
+{
+    static void rethrow( Error const & e )
+    {
+        throw bad_expected_access<Error>{ e };
+    }
+};
+
+template<>
+struct error_traits< std::exception_ptr >
+{
+    static void rethrow( std::exception_ptr const & e )
+    {
+        std::rethrow_exception( e );
+    }
+};
+
+template<>
+struct error_traits< std::error_code >
+{
+    static void rethrow( std::error_code const & e )
+    {
+        throw std::system_error( e );
+    }
+};
+
+#endif // nsel_CONFIG_NO_EXCEPTIONS
+
+#if nsel_P2505R >= 3
+namespace detail {
+
+// from https://en.cppreference.com/w/cpp/utility/expected/unexpected:
+// "the type of the unexpected value. The type must not be an array type, a non-object type, a specialization of std::unexpected, or a cv-qualified type."
+template< typename T >
+struct valid_unexpected_type : std::integral_constant< bool,
+    std::is_same< T, typename std20::remove_cvref< T >::type >::value
+    && std::is_object< T >::value
+    && !std::is_array< T >::value
+> {};
+
+template< typename T >
+struct valid_unexpected_type< unexpected_type< T > > : std::false_type {};
+
+} // namespace detail
+#endif // nsel_P2505R >= 3
+
+} // namespace expected_lite
+
+// provide future_std::unexpected_type:
+
+using expected_lite::unexpected_type;
+
+namespace expected_lite {
+
+/// class expected
+
+#if nsel_P0323R <= 2
+template< typename T, typename E = std::exception_ptr >
+class expected
+#else
+template< typename T, typename E >
+class expected
+#endif // nsel_P0323R
+{
+private:
+    template< typename, typename > friend class expected;
+
+public:
+    using value_type = T;
+    using error_type = E;
+    using unexpected_type = future_std::unexpected_type<E>;
+
+    template< typename U >
+    struct rebind
+    {
+        using type = expected<U, error_type>;
+    };
+
+    // x.x.4.1 constructors
+
+    nsel_REQUIRES_0(
+        std::is_default_constructible<T>::value
+    )
+    nsel_constexpr14 expected()
+    : contained( true )
+    {
+        contained.construct_value();
+    }
+
+    nsel_constexpr14 expected( expected const & ) = default;
+    nsel_constexpr14 expected( expected &&      ) = default;
+
+    template< typename U, typename G
+        nsel_REQUIRES_T(
+            std::is_constructible<    T, U const &>::value
+            &&  std::is_constructible<E, G const &>::value
+            && !std::is_constructible<T, expected<U, G>       &    >::value
+            && !std::is_constructible<T, expected<U, G>       &&   >::value
+            && !std::is_constructible<T, expected<U, G> const &    >::value
+            && !std::is_constructible<T, expected<U, G> const &&   >::value
+            && !std::is_convertible<     expected<U, G>       & , T>::value
+            && !std::is_convertible<     expected<U, G>       &&, T>::value
+            && !std::is_convertible<     expected<U, G> const & , T>::value
+            && !std::is_convertible<     expected<U, G> const &&, T>::value
+            && (!std::is_convertible<U const &, T>::value || !std::is_convertible<G const &, E>::value ) /*=> explicit */
+        )
+    >
+    nsel_constexpr14 explicit expected( expected<U, G> const & other )
+    : contained( other.has_value() )
+    {
+        if ( has_value() ) contained.construct_value( T{ other.contained.value() } );
+        else               contained.construct_error( E{ other.contained.error() } );
+    }
+
+    template< typename U, typename G
+        nsel_REQUIRES_T(
+            std::is_constructible<    T, U const &>::value
+            &&  std::is_constructible<E, G const &>::value
+            && !std::is_constructible<T, expected<U, G>       &    >::value
+            && !std::is_constructible<T, expected<U, G>       &&   >::value
+            && !std::is_constructible<T, expected<U, G> const &    >::value
+            && !std::is_constructible<T, expected<U, G> const &&   >::value
+            && !std::is_convertible<     expected<U, G>       & , T>::value
+            && !std::is_convertible<     expected<U, G>       &&, T>::value
+            && !std::is_convertible<     expected<U, G> const  &, T>::value
+            && !std::is_convertible<     expected<U, G> const &&, T>::value
+            && !(!std::is_convertible<U const &, T>::value || !std::is_convertible<G const &, E>::value ) /*=> non-explicit */
+        )
+    >
+    nsel_constexpr14 /*non-explicit*/ expected( expected<U, G> const & other )
+    : contained( other.has_value() )
+    {
+        if ( has_value() ) contained.construct_value( other.contained.value() );
+        else               contained.construct_error( other.contained.error() );
+    }
+
+    template< typename U, typename G
+        nsel_REQUIRES_T(
+            std::is_constructible<    T, U>::value
+            &&  std::is_constructible<E, G>::value
+            && !std::is_constructible<T, expected<U, G>       &    >::value
+            && !std::is_constructible<T, expected<U, G>       &&   >::value
+            && !std::is_constructible<T, expected<U, G> const &    >::value
+            && !std::is_constructible<T, expected<U, G> const &&   >::value
+            && !std::is_convertible<     expected<U, G>       & , T>::value
+            && !std::is_convertible<     expected<U, G>       &&, T>::value
+            && !std::is_convertible<     expected<U, G> const & , T>::value
+            && !std::is_convertible<     expected<U, G> const &&, T>::value
+            && (!std::is_convertible<U, T>::value || !std::is_convertible<G, E>::value ) /*=> explicit */
+        )
+    >
+    nsel_constexpr14 explicit expected( expected<U, G> && other )
+    : contained( other.has_value() )
+    {
+        if ( has_value() ) contained.construct_value( T{ std::move( other.contained.value() ) } );
+        else               contained.construct_error( E{ std::move( other.contained.error() ) } );
+    }
+
+    template< typename U, typename G
+        nsel_REQUIRES_T(
+            std::is_constructible<    T, U>::value
+            &&  std::is_constructible<E, G>::value
+            && !std::is_constructible<T, expected<U, G>      &     >::value
+            && !std::is_constructible<T, expected<U, G>      &&    >::value
+            && !std::is_constructible<T, expected<U, G> const &    >::value
+            && !std::is_constructible<T, expected<U, G> const &&   >::value
+            && !std::is_convertible<     expected<U, G>       & , T>::value
+            && !std::is_convertible<     expected<U, G>       &&, T>::value
+            && !std::is_convertible<     expected<U, G> const & , T>::value
+            && !std::is_convertible<     expected<U, G> const &&, T>::value
+            && !(!std::is_convertible<U, T>::value || !std::is_convertible<G, E>::value ) /*=> non-explicit */
+        )
+    >
+    nsel_constexpr14 /*non-explicit*/ expected( expected<U, G> && other )
+    : contained( other.has_value() )
+    {
+        if ( has_value() ) contained.construct_value( std::move( other.contained.value() ) );
+        else               contained.construct_error( std::move( other.contained.error() ) );
+    }
+
+    template< typename U = T
+        nsel_REQUIRES_T(
+            std::is_copy_constructible<U>::value
+        )
+    >
+    nsel_constexpr14 expected( value_type const & value )
+    : contained( true )
+    {
+        contained.construct_value( value );
+    }
+
+    template< typename U = T
+        nsel_REQUIRES_T(
+            std::is_constructible<T,U&&>::value
+            && !std::is_same<typename std20::remove_cvref<U>::type, future_std_lite_in_place_t(U)>::value
+            && !std::is_same<        expected<T,E>     , typename std20::remove_cvref<U>::type>::value
+            && !std::is_same<future_std::unexpected_type<E>, typename std20::remove_cvref<U>::type>::value
+            && !std::is_convertible<U&&,T>::value /*=> explicit */
+        )
+    >
+    nsel_constexpr14 explicit expected( U && value ) noexcept
+    (
+        std::is_nothrow_move_constructible<U>::value &&
+        std::is_nothrow_move_constructible<E>::value
+    )
+    : contained( true )
+    {
+        contained.construct_value( T{ std::forward<U>( value ) } );
+    }
+
+    template< typename U = T
+        nsel_REQUIRES_T(
+            std::is_constructible<T,U&&>::value
+            && !std::is_same<typename std20::remove_cvref<U>::type, future_std_lite_in_place_t(U)>::value
+            && !std::is_same<        expected<T,E>     , typename std20::remove_cvref<U>::type>::value
+            && !std::is_same<future_std::unexpected_type<E>, typename std20::remove_cvref<U>::type>::value
+            &&  std::is_convertible<U&&,T>::value /*=> non-explicit */
+        )
+    >
+    nsel_constexpr14 /*non-explicit*/ expected( U && value ) noexcept
+    (
+        std::is_nothrow_move_constructible<U>::value &&
+        std::is_nothrow_move_constructible<E>::value
+    )
+    : contained( true )
+    {
+        contained.construct_value( std::forward<U>( value ) );
+    }
+
+    // construct error:
+
+    template< typename G = E
+        nsel_REQUIRES_T(
+            std::is_constructible<E, G const &   >::value
+            && !std::is_convertible< G const &, E>::value /*=> explicit */
+        )
+    >
+    nsel_constexpr14 explicit expected( future_std::unexpected_type<G> const & error )
+    : contained( false )
+    {
+        contained.construct_error( E{ error.value() } );
+    }
+
+    template< typename G = E
+            nsel_REQUIRES_T(
+            std::is_constructible<E, G const &   >::value
+            && std::is_convertible<  G const &, E>::value /*=> non-explicit */
+        )
+    >
+    nsel_constexpr14 /*non-explicit*/ expected( future_std::unexpected_type<G> const & error )
+    : contained( false )
+    {
+        contained.construct_error( error.value() );
+    }
+
+    template< typename G = E
+        nsel_REQUIRES_T(
+            std::is_constructible<E, G&&   >::value
+            && !std::is_convertible< G&&, E>::value /*=> explicit */
+        )
+    >
+    nsel_constexpr14 explicit expected( future_std::unexpected_type<G> && error )
+    : contained( false )
+    {
+        contained.construct_error( E{ std::move( error.value() ) } );
+    }
+
+    template< typename G = E
+        nsel_REQUIRES_T(
+            std::is_constructible<E, G&&   >::value
+            && std::is_convertible<  G&&, E>::value /*=> non-explicit */
+        )
+    >
+    nsel_constexpr14 /*non-explicit*/ expected( future_std::unexpected_type<G> && error )
+    : contained( false )
+    {
+        contained.construct_error( std::move( error.value() ) );
+    }
+
+    // in-place construction, value
+
+    template< typename... Args
+        nsel_REQUIRES_T(
+            std::is_constructible<T, Args&&...>::value
+        )
+    >
+    nsel_constexpr14 explicit expected( future_std_lite_in_place_t(T), Args&&... args )
+    : contained( true )
+    {
+        contained.emplace_value( std::forward<Args>( args )... );
+    }
+
+    template< typename U, typename... Args
+        nsel_REQUIRES_T(
+            std::is_constructible<T, std::initializer_list<U>, Args&&...>::value
+        )
+    >
+    nsel_constexpr14 explicit expected( future_std_lite_in_place_t(T), std::initializer_list<U> il, Args&&... args )
+    : contained( true )
+    {
+        contained.emplace_value( il, std::forward<Args>( args )... );
+    }
+
+    // in-place construction, error
+
+    template< typename... Args
+        nsel_REQUIRES_T(
+            std::is_constructible<E, Args&&...>::value
+        )
+    >
+    nsel_constexpr14 explicit expected( unexpect_t, Args&&... args )
+    : contained( false )
+    {
+        contained.emplace_error( std::forward<Args>( args )... );
+    }
+
+    template< typename U, typename... Args
+        nsel_REQUIRES_T(
+            std::is_constructible<E, std::initializer_list<U>, Args&&...>::value
+        )
+    >
+    nsel_constexpr14 explicit expected( unexpect_t, std::initializer_list<U> il, Args&&... args )
+    : contained( false )
+    {
+        contained.emplace_error( il, std::forward<Args>( args )... );
+    }
+
+    // x.x.4.2 destructor
+
+    // TODO: ~expected: triviality
+    // Effects: If T is not cv void and is_trivially_destructible_v<T> is false and bool(*this), calls val.~T(). If is_trivially_destructible_v<E> is false and !bool(*this), calls unexpect.~unexpected<E>().
+    // Remarks: If either T is cv void or is_trivially_destructible_v<T> is true, and is_trivially_destructible_v<E> is true, then this destructor shall be a trivial destructor.
+
+    ~expected()
+    {
+        if ( has_value() ) contained.destruct_value();
+        else               contained.destruct_error();
+    }
+
+    // x.x.4.3 assignment
+
+    expected & operator=( expected const & other )
+    {
+        expected( other ).swap( *this );
+        return *this;
+    }
+
+    expected & operator=( expected && other ) noexcept
+    (
+        std::is_nothrow_move_constructible<   T>::value
+        && std::is_nothrow_move_assignable<   T>::value
+        && std::is_nothrow_move_constructible<E>::value     // added for missing
+        && std::is_nothrow_move_assignable<   E>::value )   //   nothrow above
+    {
+        expected( std::move( other ) ).swap( *this );
+        return *this;
+    }
+
+    template< typename U
+        nsel_REQUIRES_T(
+            !std::is_same<expected<T,E>, typename std20::remove_cvref<U>::type>::value
+            && std17::conjunction<std::is_scalar<T>, std::is_same<T, std::decay<U>> >::value
+            && std::is_constructible<T ,U>::value
+            && std::is_assignable<   T&,U>::value
+            && std::is_nothrow_move_constructible<E>::value )
+    >
+    expected & operator=( U && value )
+    {
+        expected( std::forward<U>( value ) ).swap( *this );
+        return *this;
+    }
+
+    template< typename G = E
+        nsel_REQUIRES_T(
+            std::is_constructible<E, G const&>::value &&
+            std::is_copy_constructible<G>::value    // TODO: std::is_nothrow_copy_constructible<G>
+            && std::is_copy_assignable<G>::value
+        )
+    >
+    expected & operator=( future_std::unexpected_type<G> const & error )
+    {
+        expected( unexpect, error.value() ).swap( *this );
+        return *this;
+    }
+
+    template< typename G = E
+        nsel_REQUIRES_T(
+            std::is_constructible<E, G&&>::value &&
+            std::is_move_constructible<G>::value    // TODO: std::is_nothrow_move_constructible<G>
+            && std::is_move_assignable<G>::value
+        )
+    >
+    expected & operator=( future_std::unexpected_type<G> && error )
+    {
+        expected( unexpect, std::move( error.value() ) ).swap( *this );
+        return *this;
+    }
+
+    template< typename... Args
+        nsel_REQUIRES_T(
+            std::is_nothrow_constructible<T, Args&&...>::value
+        )
+    >
+    value_type & emplace( Args &&... args )
+    {
+        expected( future_std_lite_in_place(T), std::forward<Args>(args)... ).swap( *this );
+        return value();
+    }
+
+    template< typename U, typename... Args
+        nsel_REQUIRES_T(
+            std::is_nothrow_constructible<T, std::initializer_list<U>&, Args&&...>::value
+        )
+    >
+    value_type & emplace( std::initializer_list<U> il, Args &&... args )
+    {
+        expected( future_std_lite_in_place(T), il, std::forward<Args>(args)... ).swap( *this );
+        return value();
+    }
+
+    // x.x.4.4 swap
+
+    template< typename U=T, typename G=E >
+    nsel_REQUIRES_R( void,
+        std17::is_swappable<   U>::value
+        && std17::is_swappable<G>::value
+        && ( std::is_move_constructible<U>::value || std::is_move_constructible<G>::value )
+    )
+    swap( expected & other ) noexcept
+    (
+        std::is_nothrow_move_constructible<T>::value && std17::is_nothrow_swappable<T&>::value &&
+        std::is_nothrow_move_constructible<E>::value && std17::is_nothrow_swappable<E&>::value
+    )
+    {
+        using std::swap;
+
+        if      (   bool(*this) &&   bool(other) ) { swap( contained.value(), other.contained.value() ); }
+        else if ( ! bool(*this) && ! bool(other) ) { swap( contained.error(), other.contained.error() ); }
+        else if (   bool(*this) && ! bool(other) ) { error_type t( std::move( other.error() ) );
+                                                     other.contained.destruct_error();
+                                                     other.contained.construct_value( std::move( contained.value() ) );
+                                                     contained.destruct_value();
+                                                     contained.construct_error( std::move( t ) );
+                                                     bool has_value = contained.has_value();
+                                                     bool other_has_value = other.has_value();
+                                                     other.contained.set_has_value(has_value);
+                                                     contained.set_has_value(other_has_value);
+                                                   }
+        else if ( ! bool(*this) &&   bool(other) ) { other.swap( *this ); }
+    }
+
+    // x.x.4.5 observers
+
+    constexpr value_type const * operator ->() const
+    {
+        return assert( has_value() ), contained.value_ptr();
+    }
+
+    value_type * operator ->()
+    {
+        return assert( has_value() ), contained.value_ptr();
+    }
+
+    constexpr value_type const & operator *() const &
+    {
+        return assert( has_value() ), contained.value();
+    }
+
+    value_type & operator *() &
+    {
+        return assert( has_value() ), contained.value();
+    }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+
+    constexpr value_type const && operator *() const &&
+    {
+        return std::move( ( assert( has_value() ), contained.value() ) );
+    }
+
+    nsel_constexpr14 value_type && operator *() &&
+    {
+        return std::move( ( assert( has_value() ), contained.value() ) );
+    }
+
+#endif
+
+    constexpr explicit operator bool() const noexcept
+    {
+        return has_value();
+    }
+
+    constexpr bool has_value() const noexcept
+    {
+        return contained.has_value();
+    }
+
+    constexpr value_type const & value() const &
+    {
+        return has_value()
+            ? ( contained.value() )
+            : ( error_traits<error_type>::rethrow( contained.error() ), contained.value() );
+    }
+
+    value_type & value() &
+    {
+        return has_value()
+            ? ( contained.value() )
+            : ( error_traits<error_type>::rethrow( contained.error() ), contained.value() );
+    }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+
+    constexpr value_type const && value() const &&
+    {
+        return std::move( has_value()
+            ? ( contained.value() )
+            : ( error_traits<error_type>::rethrow( contained.error() ), contained.value() ) );
+    }
+
+    nsel_constexpr14 value_type && value() &&
+    {
+        return std::move( has_value()
+            ? ( contained.value() )
+            : ( error_traits<error_type>::rethrow( contained.error() ), contained.value() ) );
+    }
+
+#endif
+
+    constexpr error_type const & error() const &
+    {
+        return assert( ! has_value() ), contained.error();
+    }
+
+    error_type & error() &
+    {
+        return assert( ! has_value() ), contained.error();
+    }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+
+    constexpr error_type const && error() const &&
+    {
+        return std::move( ( assert( ! has_value() ), contained.error() ) );
+    }
+
+    error_type && error() &&
+    {
+        return std::move( ( assert( ! has_value() ), contained.error() ) );
+    }
+
+#endif
+
+    constexpr unexpected_type get_unexpected() const
+    {
+        return make_unexpected( contained.error() );
+    }
+
+    template< typename Ex >
+    bool has_exception() const
+    {
+        using ContainedEx = typename std::remove_reference< decltype( get_unexpected().value() ) >::type;
+        return ! has_value() && std::is_base_of< Ex, ContainedEx>::value;
+    }
+
+    template< typename U
+        nsel_REQUIRES_T(
+            std::is_copy_constructible< T>::value
+            && std::is_convertible<U&&, T>::value
+        )
+    >
+    value_type value_or( U && v ) const &
+    {
+        return has_value()
+            ? contained.value()
+            : static_cast<T>( std::forward<U>( v ) );
+    }
+
+    template< typename U
+        nsel_REQUIRES_T(
+            std::is_move_constructible< T>::value
+            && std::is_convertible<U&&, T>::value
+        )
+    >
+    value_type value_or( U && v ) &&
+    {
+        return has_value()
+            ? std::move( contained.value() )
+            : static_cast<T>( std::forward<U>( v ) );
+    }
+
+#if nsel_P2505R >= 4
+    template< typename G = E
+        nsel_REQUIRES_T(
+            std::is_copy_constructible< E >::value
+            && std::is_convertible< G, E >::value
+        )
+    >
+    nsel_constexpr error_type error_or( G && e ) const &
+    {
+        return has_value()
+            ? static_cast< E >( std::forward< G >( e ) )
+            : contained.error();
+    }
+
+    template< typename G = E
+        nsel_REQUIRES_T(
+            std::is_move_constructible< E >::value
+            && std::is_convertible< G, E >::value
+        )
+    >
+    nsel_constexpr14 error_type error_or( G && e ) &&
+    {
+        return has_value()
+            ? static_cast< E >( std::forward< G >( e ) )
+            : std::move( contained.error() );
+    }
+#endif // nsel_P2505R >= 4
+
+#if nsel_P2505R >= 3
+    // Monadic operations (P2505)
+    template< typename F
+        nsel_REQUIRES_T(
+            detail::is_expected < detail::invoke_result_nocvref_t< F, value_type & > > ::value
+            && std::is_same< typename detail::invoke_result_nocvref_t< F, value_type & >::error_type, error_type >::value
+            && std::is_constructible< error_type, error_type & >::value
+        )
+    >
+    nsel_constexpr14 detail::invoke_result_nocvref_t< F, value_type & > and_then( F && f ) &
+    {
+        return has_value()
+            ? detail::invoke_result_nocvref_t< F, value_type & >( detail::invoke( std::forward< F >( f ), value() ) )
+            : detail::invoke_result_nocvref_t< F, value_type & >( unexpect, error() );
+    }
+
+    template<typename F
+        nsel_REQUIRES_T(
+            detail::is_expected< detail::invoke_result_nocvref_t< F, const value_type & > >::value
+            && std::is_same< typename detail::invoke_result_nocvref_t< F, const value_type & >::error_type, error_type >::value
+            && std::is_constructible< error_type, const error_type & >::value
+        )
+    >
+    nsel_constexpr detail::invoke_result_nocvref_t< F, const value_type & > and_then( F && f ) const &
+    {
+        return has_value()
+            ? detail::invoke_result_nocvref_t< F, const value_type & >( detail::invoke( std::forward< F >( f ), value() ) )
+            : detail::invoke_result_nocvref_t< F, const value_type & >( unexpect, error() );
+    }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+    template<typename F
+        nsel_REQUIRES_T(
+            detail::is_expected< detail::invoke_result_nocvref_t< F, value_type && > >::value
+            && std::is_same< typename detail::invoke_result_nocvref_t< F, value_type && >::error_type, error_type >::value
+            && std::is_constructible< error_type, error_type && >::value
+        )
+    >
+    nsel_constexpr14 detail::invoke_result_nocvref_t< F, value_type && > and_then( F && f ) &&
+    {
+        return has_value()
+            ? detail::invoke_result_nocvref_t< F, value_type && >( detail::invoke( std::forward< F >( f ), std::move( value() ) ) )
+            : detail::invoke_result_nocvref_t< F, value_type && >( unexpect, std::move( error() ) );
+    }
+
+    template<typename F
+        nsel_REQUIRES_T(
+            detail::is_expected< detail::invoke_result_nocvref_t< F, const value_type && > >::value
+            && std::is_same< typename detail::invoke_result_nocvref_t< F, const value_type & >::error_type, error_type >::value
+            && std::is_constructible< error_type, const error_type && >::value
+        )
+    >
+    nsel_constexpr detail::invoke_result_nocvref_t< F, const value_type && > and_then( F && f ) const &&
+    {
+        return has_value()
+            ? detail::invoke_result_nocvref_t< F, const value_type && >( detail::invoke( std::forward< F >( f ), std::move( value() ) ) )
+            : detail::invoke_result_nocvref_t< F, const value_type && >( unexpect, std::move( error() ) );
+    }
+#endif
+
+    template<typename F
+        nsel_REQUIRES_T(
+            detail::is_expected< detail::invoke_result_nocvref_t< F, error_type & > >::value
+            && std::is_same< typename detail::invoke_result_nocvref_t< F, error_type & >::value_type, value_type >::value
+            && std::is_constructible< value_type, value_type & >::value
+        )
+    >
+    nsel_constexpr14 detail::invoke_result_nocvref_t< F, error_type & > or_else( F && f ) &
+    {
+        return has_value()
+            ? detail::invoke_result_nocvref_t< F, error_type & >( value() )
+            : detail::invoke_result_nocvref_t< F, error_type & >( detail::invoke( std::forward< F >( f ), error() ) );
+    }
+
+    template<typename F
+        nsel_REQUIRES_T(
+            detail::is_expected< detail::invoke_result_nocvref_t< F, const error_type & > >::value
+            && std::is_same< typename detail::invoke_result_nocvref_t< F, const error_type & >::value_type, value_type >::value
+            && std::is_constructible< value_type, const value_type & >::value
+        )
+    >
+    nsel_constexpr detail::invoke_result_nocvref_t< F, const error_type & > or_else( F && f ) const &
+    {
+        return has_value()
+            ? detail::invoke_result_nocvref_t< F, const error_type & >( value() )
+            : detail::invoke_result_nocvref_t< F, const error_type & >( detail::invoke( std::forward< F >( f ), error() ) );
+    }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+    template<typename F
+        nsel_REQUIRES_T(
+            detail::is_expected< detail::invoke_result_nocvref_t< F, error_type && > >::value
+            && std::is_same< typename detail::invoke_result_nocvref_t< F, error_type && >::value_type, value_type >::value
+            && std::is_constructible< value_type, value_type && >::value
+        )
+    >
+    nsel_constexpr14 detail::invoke_result_nocvref_t< F, error_type && > or_else( F && f ) &&
+    {
+        return has_value()
+            ? detail::invoke_result_nocvref_t< F, error_type && >( std::move( value() ) )
+            : detail::invoke_result_nocvref_t< F, error_type && >( detail::invoke( std::forward< F >( f ), std::move( error() ) ) );
+    }
+
+    template<typename F
+        nsel_REQUIRES_T(
+            detail::is_expected< detail::invoke_result_nocvref_t< F, const error_type && > >::value
+            && std::is_same< typename detail::invoke_result_nocvref_t< F, const error_type && >::value_type, value_type >::value
+            && std::is_constructible< value_type, const value_type && >::value
+        )
+    >
+    nsel_constexpr detail::invoke_result_nocvref_t< F, const error_type && > or_else( F && f ) const &&
+    {
+        return has_value()
+            ? detail::invoke_result_nocvref_t< F, const error_type && >( std::move( value() ) )
+            : detail::invoke_result_nocvref_t< F, const error_type && >( detail::invoke( std::forward< F >( f ), std::move( error() ) ) );
+    }
+#endif
+
+    template<typename F
+        nsel_REQUIRES_T(
+            std::is_constructible< error_type, error_type & >::value
+            && !std::is_void< detail::transform_invoke_result_t< F, value_type & > >::value
+            && detail::valid_expected_value_type< detail::transform_invoke_result_t< F, value_type & > >::value
+        )
+    >
+    nsel_constexpr14 expected< detail::transform_invoke_result_t< F, value_type & >, error_type > transform( F && f ) &
+    {
+        return has_value()
+            ? expected< detail::transform_invoke_result_t< F, value_type & >, error_type >( detail::invoke( std::forward< F >( f ), **this ) )
+            : make_unexpected( error() );
+    }
+
+    template<typename F
+        nsel_REQUIRES_T(
+            std::is_constructible< error_type, error_type & >::value
+            && std::is_void< detail::transform_invoke_result_t< F, value_type & > >::value
+        )
+    >
+    nsel_constexpr14 expected< void, error_type > transform( F && f ) &
+    {
+        return has_value()
+            ? ( detail::invoke( std::forward< F >( f ), **this ), expected< void, error_type >() )
+            : make_unexpected( error() );
+    }
+
+    template<typename F
+        nsel_REQUIRES_T(
+            std::is_constructible< error_type, const error_type & >::value
+            && !std::is_void< detail::transform_invoke_result_t< F, const value_type & > >::value
+            && detail::valid_expected_value_type< detail::transform_invoke_result_t< F, const value_type & > >::value
+        )
+    >
+    nsel_constexpr expected< detail::transform_invoke_result_t< F, const value_type & >, error_type > transform( F && f ) const &
+    {
+        return has_value()
+            ? expected< detail::transform_invoke_result_t< F, const value_type & >, error_type >( detail::invoke( std::forward< F >( f ), **this ) )
+            : make_unexpected( error() );
+    }
+
+    template<typename F
+        nsel_REQUIRES_T(
+            std::is_constructible< error_type, const error_type & >::value
+            && std::is_void< detail::transform_invoke_result_t< F, const value_type & > >::value
+        )
+    >
+    nsel_constexpr expected< void, error_type > transform( F && f ) const &
+    {
+        return has_value()
+            ? ( detail::invoke( std::forward< F >( f ), **this ), expected< void, error_type >() )
+            : make_unexpected( error() );
+    }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+    template<typename F
+        nsel_REQUIRES_T(
+            std::is_constructible< error_type, error_type && >::value
+            && !std::is_void< detail::transform_invoke_result_t< F, value_type && > >::value
+            && detail::valid_expected_value_type< detail::transform_invoke_result_t< F, value_type && > >::value
+        )
+    >
+    nsel_constexpr14 expected< detail::transform_invoke_result_t< F, value_type && >, error_type > transform( F && f ) &&
+    {
+        return has_value()
+            ? expected< detail::transform_invoke_result_t< F, value_type && >, error_type >( detail::invoke( std::forward< F >( f ), std::move( **this ) ) )
+            : make_unexpected( std::move( error() ) );
+    }
+
+    template<typename F
+        nsel_REQUIRES_T(
+            std::is_constructible< error_type, error_type && >::value
+            && std::is_void< detail::transform_invoke_result_t< F, value_type && > >::value
+        )
+    >
+    nsel_constexpr14 expected< void, error_type > transform( F && f ) &&
+    {
+        return has_value()
+            ? ( detail::invoke( std::forward< F >( f ), **this ), expected< void, error_type >() )
+            : make_unexpected( std::move( error() ) );
+    }
+
+    template<typename F
+        nsel_REQUIRES_T(
+            std::is_constructible< error_type, const error_type && >::value
+            && !std::is_void< detail::transform_invoke_result_t< F, const value_type && > >::value
+            && detail::valid_expected_value_type< detail::transform_invoke_result_t< F, const value_type && > >::value
+        )
+    >
+    nsel_constexpr expected< detail::transform_invoke_result_t< F, const value_type && >, error_type > transform( F && f ) const &&
+    {
+        return has_value()
+            ? expected< detail::transform_invoke_result_t< F, const value_type && >, error_type >( detail::invoke( std::forward< F >( f ), std::move( **this ) ) )
+            : make_unexpected( std::move( error() ) );
+    }
+
+    template<typename F
+        nsel_REQUIRES_T(
+            std::is_constructible< error_type, const error_type && >::value
+            && std::is_void< detail::transform_invoke_result_t< F, const value_type && > >::value
+        )
+    >
+    nsel_constexpr expected< void, error_type > transform( F && f ) const &&
+    {
+        return has_value()
+            ? ( detail::invoke( std::forward< F >( f ), **this ), expected< void, error_type >() )
+            : make_unexpected( std::move( error() ) );
+    }
+#endif
+
+    template<typename F
+        nsel_REQUIRES_T(
+            detail::valid_unexpected_type< detail::transform_invoke_result_t< F, error_type & > >::value
+            && std::is_constructible< value_type, value_type & >::value
+        )
+    >
+    nsel_constexpr14 expected< value_type, detail::transform_invoke_result_t< F, error_type & > > transform_error( F && f ) &
+    {
+        return has_value()
+            ? expected< value_type, detail::transform_invoke_result_t< F, error_type & > >( in_place, **this )
+            : make_unexpected( detail::invoke( std::forward< F >( f ), error() ) );
+    }
+
+    template<typename F
+        nsel_REQUIRES_T(
+            detail::valid_unexpected_type< detail::transform_invoke_result_t< F, const error_type & > >::value
+            && std::is_constructible< value_type, const value_type & >::value
+        )
+    >
+    nsel_constexpr expected< value_type, detail::transform_invoke_result_t< F, const error_type & > > transform_error( F && f ) const &
+    {
+        return has_value()
+            ? expected< value_type, detail::transform_invoke_result_t< F, const error_type & > >( in_place, **this )
+            : make_unexpected( detail::invoke( std::forward< F >( f ), error() ) );
+    }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+    template<typename F
+        nsel_REQUIRES_T(
+            detail::valid_unexpected_type< detail::transform_invoke_result_t< F, error_type && > >::value
+            && std::is_constructible< value_type, value_type && >::value
+        )
+    >
+    nsel_constexpr14 expected< value_type, detail::transform_invoke_result_t< F, error_type && > > transform_error( F && f ) &&
+    {
+        return has_value()
+            ? expected< value_type, detail::transform_invoke_result_t< F, error_type && > >( in_place, std::move( **this ) )
+            : make_unexpected( detail::invoke( std::forward< F >( f ), std::move( error() ) ) );
+    }
+
+    template<typename F
+        nsel_REQUIRES_T(
+            detail::valid_unexpected_type< detail::transform_invoke_result_t< F, const error_type && > >::value
+            && std::is_constructible< value_type, const value_type && >::value
+        )
+    >
+    nsel_constexpr expected< value_type, detail::transform_invoke_result_t< F, const error_type && > > transform_error( F && f ) const &&
+    {
+        return has_value()
+            ? expected< value_type, detail::transform_invoke_result_t< F, const error_type && > >( in_place, std::move( **this ) )
+            : make_unexpected( detail::invoke( std::forward< F >( f ), std::move( error() ) ) );
+    }
+#endif
+#endif // nsel_P2505R >= 3
+    // unwrap()
+
+//  template <class U, class E>
+//  constexpr expected<U,E> expected<expected<U,E>,E>::unwrap() const&;
+
+//  template <class T, class E>
+//  constexpr expected<T,E> expected<T,E>::unwrap() const&;
+
+//  template <class U, class E>
+//  expected<U,E> expected<expected<U,E>, E>::unwrap() &&;
+
+//  template <class T, class E>
+//  template expected<T,E> expected<T,E>::unwrap() &&;
+
+    // factories
+
+//  template< typename Ex, typename F>
+//  expected<T,E> catch_exception(F&& f);
+
+//  template< typename F>
+//  expected<decltype(func(declval<T>())),E> map(F&& func) ;
+
+//  template< typename F>
+//  'see below' bind(F&& func);
+
+//  template< typename F>
+//  expected<T,E> catch_error(F&& f);
+
+//  template< typename F>
+//  'see below' then(F&& func);
+
+private:
+    detail::storage_t
+    <
+        T
+        ,E
+        , std::is_copy_constructible<T>::value && std::is_copy_constructible<E>::value
+        , std::is_move_constructible<T>::value && std::is_move_constructible<E>::value
+    >
+    contained;
+};
+
+/// class expected, void specialization
+
+template< typename E >
+class expected<void, E>
+{
+private:
+    template< typename, typename > friend class expected;
+
+public:
+    using value_type = void;
+    using error_type = E;
+    using unexpected_type = future_std::unexpected_type<E>;
+
+    // x.x.4.1 constructors
+
+    constexpr expected() noexcept
+        : contained( true )
+    {}
+
+    nsel_constexpr14 expected( expected const & other ) = default;
+    nsel_constexpr14 expected( expected &&      other ) = default;
+
+    constexpr explicit expected( future_std_lite_in_place_t(void) )
+        : contained( true )
+    {}
+
+    template< typename G = E
+        nsel_REQUIRES_T(
+            !std::is_convertible<G const &, E>::value /*=> explicit */
+        )
+    >
+    nsel_constexpr14 explicit expected( future_std::unexpected_type<G> const & error )
+        : contained( false )
+    {
+        contained.construct_error( E{ error.value() } );
+    }
+
+    template< typename G = E
+        nsel_REQUIRES_T(
+            std::is_convertible<G const &, E>::value /*=> non-explicit */
+        )
+    >
+    nsel_constexpr14 /*non-explicit*/ expected( future_std::unexpected_type<G> const & error )
+        : contained( false )
+    {
+        contained.construct_error( error.value() );
+    }
+
+    template< typename G = E
+        nsel_REQUIRES_T(
+            !std::is_convertible<G&&, E>::value /*=> explicit */
+        )
+    >
+    nsel_constexpr14 explicit expected( future_std::unexpected_type<G> && error )
+        : contained( false )
+    {
+        contained.construct_error( E{ std::move( error.value() ) } );
+    }
+
+    template< typename G = E
+        nsel_REQUIRES_T(
+            std::is_convertible<G&&, E>::value /*=> non-explicit */
+        )
+    >
+    nsel_constexpr14 /*non-explicit*/ expected( future_std::unexpected_type<G> && error )
+        : contained( false )
+    {
+        contained.construct_error( std::move( error.value() ) );
+    }
+
+    template< typename... Args
+        nsel_REQUIRES_T(
+            std::is_constructible<E, Args&&...>::value
+        )
+    >
+    nsel_constexpr14 explicit expected( unexpect_t, Args&&... args )
+        : contained( false )
+    {
+        contained.emplace_error( std::forward<Args>( args )... );
+    }
+
+    template< typename U, typename... Args
+        nsel_REQUIRES_T(
+            std::is_constructible<E, std::initializer_list<U>, Args&&...>::value
+        )
+    >
+    nsel_constexpr14 explicit expected( unexpect_t, std::initializer_list<U> il, Args&&... args )
+        : contained( false )
+    {
+        contained.emplace_error( il, std::forward<Args>( args )... );
+    }
+
+    // destructor
+
+    ~expected()
+    {
+        if ( ! has_value() )
+        {
+            contained.destruct_error();
+        }
+    }
+
+    // x.x.4.3 assignment
+
+    expected & operator=( expected const & other )
+    {
+        expected( other ).swap( *this );
+        return *this;
+    }
+
+    expected & operator=( expected && other ) noexcept
+    (
+        std::is_nothrow_move_assignable<E>::value &&
+        std::is_nothrow_move_constructible<E>::value )
+    {
+        expected( std::move( other ) ).swap( *this );
+        return *this;
+    }
+
+    void emplace()
+    {
+        expected().swap( *this );
+    }
+
+    // x.x.4.4 swap
+
+    template< typename G = E >
+    nsel_REQUIRES_R( void,
+        std17::is_swappable<G>::value
+        && std::is_move_constructible<G>::value
+    )
+    swap( expected & other ) noexcept
+    (
+        std::is_nothrow_move_constructible<E>::value && std17::is_nothrow_swappable<E&>::value
+    )
+    {
+        using std::swap;
+
+        if      ( ! bool(*this) && ! bool(other) ) { swap( contained.error(), other.contained.error() ); }
+        else if (   bool(*this) && ! bool(other) ) { contained.construct_error( std::move( other.error() ) );
+                                                     bool has_value = contained.has_value();
+                                                     bool other_has_value = other.has_value();
+                                                     other.contained.set_has_value(has_value);
+                                                     contained.set_has_value(other_has_value);
+                                                     }
+        else if ( ! bool(*this) &&   bool(other) ) { other.swap( *this ); }
+    }
+
+    // x.x.4.5 observers
+
+    constexpr explicit operator bool() const noexcept
+    {
+        return has_value();
+    }
+
+    constexpr bool has_value() const noexcept
+    {
+        return contained.has_value();
+    }
+
+    void value() const
+    {
+        if ( ! has_value() )
+        {
+            error_traits<error_type>::rethrow( contained.error() );
+        }
+    }
+
+    constexpr error_type const & error() const &
+    {
+        return assert( ! has_value() ), contained.error();
+    }
+
+    error_type & error() &
+    {
+        return assert( ! has_value() ), contained.error();
+    }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+
+    constexpr error_type const && error() const &&
+    {
+        return std::move( ( assert( ! has_value() ), contained.error() ) );
+    }
+
+    error_type && error() &&
+    {
+        return std::move( ( assert( ! has_value() ), contained.error() ) );
+    }
+
+#endif
+
+    constexpr unexpected_type get_unexpected() const
+    {
+        return make_unexpected( contained.error() );
+    }
+
+    template< typename Ex >
+    bool has_exception() const
+    {
+        using ContainedEx = typename std::remove_reference< decltype( get_unexpected().value() ) >::type;
+        return ! has_value() && std::is_base_of< Ex, ContainedEx>::value;
+    }
+
+#if nsel_P2505R >= 4
+    template< typename G = E
+        nsel_REQUIRES_T(
+            std::is_copy_constructible< E >::value
+            && std::is_convertible< G, E >::value
+        )
+    >
+    nsel_constexpr error_type error_or( G && e ) const &
+    {
+        return has_value()
+            ? static_cast< E >( std::forward< G >( e ) )
+            : contained.error();
+    }
+
+    template< typename G = E
+        nsel_REQUIRES_T(
+            std::is_move_constructible< E >::value
+            && std::is_convertible< G, E >::value
+        )
+    >
+    nsel_constexpr14 error_type error_or( G && e ) &&
+    {
+        return has_value()
+            ? static_cast< E >( std::forward< G >( e ) )
+            : std::move( contained.error() );
+    }
+#endif // nsel_P2505R >= 4
+
+#if nsel_P2505R >= 3
+    // Monadic operations (P2505)
+    template<typename F
+        nsel_REQUIRES_T(
+            detail::is_expected< detail::invoke_result_nocvref_t< F > >::value
+            && std::is_same< typename detail::invoke_result_nocvref_t< F >::error_type, error_type >::value
+            && std::is_constructible< error_type, error_type & >::value
+        )
+    >
+    nsel_constexpr14 detail::invoke_result_nocvref_t< F > and_then( F && f ) &
+    {
+        return has_value()
+            ? detail::invoke_result_nocvref_t< F >( detail::invoke( std::forward< F >( f ) ) )
+            : detail::invoke_result_nocvref_t< F >( unexpect, error() );
+    }
+
+    template<typename F
+        nsel_REQUIRES_T(
+            detail::is_expected< detail::invoke_result_nocvref_t< F > >::value
+            && std::is_same< typename detail::invoke_result_nocvref_t< F >::error_type, error_type >::value
+            && std::is_constructible< error_type, const error_type & >::value
+        )
+    >
+    nsel_constexpr detail::invoke_result_nocvref_t< F > and_then( F && f ) const &
+    {
+        return has_value()
+            ? detail::invoke_result_nocvref_t< F >( detail::invoke( std::forward< F >( f ) ) )
+            : detail::invoke_result_nocvref_t< F >( unexpect, error() );
+    }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+    template<typename F
+        nsel_REQUIRES_T(
+            detail::is_expected< detail::invoke_result_nocvref_t< F > >::value
+            && std::is_same< typename detail::invoke_result_nocvref_t< F >::error_type, error_type >::value
+            && std::is_constructible< error_type, error_type && >::value
+        )
+    >
+    nsel_constexpr14 detail::invoke_result_nocvref_t< F > and_then( F && f ) &&
+    {
+        return has_value()
+            ? detail::invoke_result_nocvref_t< F >( detail::invoke( std::forward< F >( f ) ) )
+            : detail::invoke_result_nocvref_t< F >( unexpect, std::move( error() ) );
+    }
+
+    template<typename F
+        nsel_REQUIRES_T(
+            detail::is_expected< detail::invoke_result_nocvref_t< F > >::value
+            && std::is_same< typename detail::invoke_result_nocvref_t< F >::error_type, error_type >::value
+            && std::is_constructible< error_type, const error_type && >::value
+        )
+    >
+    nsel_constexpr detail::invoke_result_nocvref_t< F > and_then( F && f ) const &&
+    {
+        return has_value()
+            ? detail::invoke_result_nocvref_t< F >( detail::invoke( std::forward< F >( f ) ) )
+            : detail::invoke_result_nocvref_t< F >( unexpect, std::move( error() ) );
+    }
+#endif
+
+    template<typename F
+        nsel_REQUIRES_T(
+            detail::is_expected< detail::invoke_result_nocvref_t< F, error_type & > >::value
+            && std::is_void< typename detail::invoke_result_nocvref_t< F, error_type & >::value_type >::value
+        )
+    >
+    nsel_constexpr14 detail::invoke_result_nocvref_t< F, error_type & > or_else( F && f ) &
+    {
+        return has_value()
+            ? detail::invoke_result_nocvref_t< F, error_type & >()
+            : detail::invoke_result_nocvref_t< F, error_type & >( detail::invoke( std::forward< F >( f ), error() ) );
+    }
+
+    template<typename F
+        nsel_REQUIRES_T(
+            detail::is_expected< detail::invoke_result_nocvref_t< F, const error_type & > >::value
+            && std::is_void< typename detail::invoke_result_nocvref_t< F, const error_type & >::value_type >::value
+        )
+    >
+    nsel_constexpr detail::invoke_result_nocvref_t< F, const error_type & > or_else( F && f ) const &
+    {
+        return has_value()
+            ? detail::invoke_result_nocvref_t< F, const error_type & >()
+            : detail::invoke_result_nocvref_t< F, const error_type & >( detail::invoke( std::forward< F >( f ), error() ) );
+    }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+    template<typename F
+        nsel_REQUIRES_T(
+            detail::is_expected< detail::invoke_result_nocvref_t< F, error_type && > >::value
+            && std::is_void< typename detail::invoke_result_nocvref_t< F, error_type && >::value_type >::value
+        )
+    >
+    nsel_constexpr14 detail::invoke_result_nocvref_t< F, error_type && > or_else( F && f ) &&
+    {
+        return has_value()
+            ? detail::invoke_result_nocvref_t< F, error_type && >()
+            : detail::invoke_result_nocvref_t< F, error_type && >( detail::invoke( std::forward< F >( f ), std::move( error() ) ) );
+    }
+
+    template<typename F
+        nsel_REQUIRES_T(
+            detail::is_expected< detail::invoke_result_nocvref_t< F, const error_type && > >::value
+            && std::is_void< typename detail::invoke_result_nocvref_t< F, const error_type && >::value_type >::value
+        )
+    >
+    nsel_constexpr detail::invoke_result_nocvref_t< F, const error_type && > or_else( F && f ) const &&
+    {
+        return has_value()
+            ? detail::invoke_result_nocvref_t< F, const error_type && >()
+            : detail::invoke_result_nocvref_t< F, const error_type && >( detail::invoke( std::forward< F >( f ), std::move( error() ) ) );
+    }
+#endif
+
+    template<typename F
+        nsel_REQUIRES_T(
+            std::is_constructible< error_type, error_type & >::value
+            && !std::is_void< detail::transform_invoke_result_t< F > >::value
+        )
+    >
+    nsel_constexpr14 expected< detail::transform_invoke_result_t< F >, error_type > transform( F && f ) &
+    {
+        return has_value()
+            ? expected< detail::transform_invoke_result_t< F >, error_type >( detail::invoke( std::forward< F >( f ) ) )
+            : make_unexpected( error() );
+    }
+
+    template<typename F
+        nsel_REQUIRES_T(
+            std::is_constructible< error_type, error_type & >::value
+            && std::is_void< detail::transform_invoke_result_t< F > >::value
+        )
+    >
+    nsel_constexpr14 expected< void, error_type > transform( F && f ) &
+    {
+        return has_value()
+            ? ( detail::invoke( std::forward< F >( f ) ), expected< void, error_type >() )
+            : make_unexpected( error() );
+    }
+
+    template<typename F
+        nsel_REQUIRES_T(
+            std::is_constructible< error_type, const error_type & >::value
+            && !std::is_void< detail::transform_invoke_result_t< F > >::value
+        )
+    >
+    nsel_constexpr expected< detail::transform_invoke_result_t< F >, error_type > transform( F && f ) const &
+    {
+        return has_value()
+            ? expected< detail::transform_invoke_result_t< F >, error_type >( detail::invoke( std::forward< F >( f ) ) )
+            : make_unexpected( error() );
+    }
+
+    template<typename F
+        nsel_REQUIRES_T(
+            std::is_constructible< error_type, const error_type & >::value
+            && std::is_void< detail::transform_invoke_result_t< F > >::value
+        )
+    >
+    nsel_constexpr expected< void, error_type > transform( F && f ) const &
+    {
+        return has_value()
+            ? ( detail::invoke( std::forward< F >( f ) ), expected< void, error_type >() )
+            : make_unexpected( error() );
+    }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+    template<typename F
+        nsel_REQUIRES_T(
+            std::is_constructible< error_type, error_type && >::value
+            && !std::is_void< detail::transform_invoke_result_t< F > >::value
+        )
+    >
+    nsel_constexpr14 expected< detail::transform_invoke_result_t< F >, error_type > transform( F && f ) &&
+    {
+        return has_value()
+            ? expected< detail::transform_invoke_result_t< F >, error_type >( detail::invoke( std::forward< F >( f ) ) )
+            : make_unexpected( error() );
+    }
+
+    template<typename F
+        nsel_REQUIRES_T(
+            std::is_constructible< error_type, error_type && >::value
+            && std::is_void< detail::transform_invoke_result_t< F > >::value
+        )
+    >
+    nsel_constexpr14 expected< void, error_type > transform( F && f ) &&
+    {
+        return has_value()
+            ? ( detail::invoke( std::forward< F >( f ) ), expected< void, error_type >() )
+            : make_unexpected( error() );
+    }
+
+    template<typename F
+        nsel_REQUIRES_T(
+            std::is_constructible< error_type, const error_type && >::value
+            && !std::is_void< detail::transform_invoke_result_t< F > >::value
+        )
+    >
+    nsel_constexpr expected< detail::transform_invoke_result_t< F >, error_type > transform( F && f ) const &&
+    {
+        return has_value()
+            ? expected< detail::transform_invoke_result_t< F >, error_type >( detail::invoke( std::forward< F >( f ) ) )
+            : make_unexpected( error() );
+    }
+
+    template<typename F
+        nsel_REQUIRES_T(
+            std::is_constructible< error_type, const error_type && >::value
+            && std::is_void< detail::transform_invoke_result_t< F > >::value
+        )
+    >
+    nsel_constexpr expected< void, error_type > transform( F && f ) const &&
+    {
+        return has_value()
+            ? ( detail::invoke( std::forward< F >( f ) ), expected< void, error_type >() )
+            : make_unexpected( error() );
+    }
+#endif
+
+    template<typename F
+        nsel_REQUIRES_T(
+            detail::valid_unexpected_type< detail::transform_invoke_result_t< F, error_type & > >::value
+        )
+    >
+    nsel_constexpr14 expected< void, detail::transform_invoke_result_t< F, error_type & > > transform_error( F && f ) &
+    {
+        return has_value()
+            ? expected< void, detail::transform_invoke_result_t< F, error_type & > >()
+            : make_unexpected( detail::invoke( std::forward< F >( f ), error() ) );
+    }
+
+    template<typename F
+        nsel_REQUIRES_T(
+            detail::valid_unexpected_type< detail::transform_invoke_result_t< F, const error_type & > >::value
+        )
+    >
+    nsel_constexpr expected< void, detail::transform_invoke_result_t< F, const error_type & > > transform_error( F && f ) const &
+    {
+        return has_value()
+            ? expected< void, detail::transform_invoke_result_t< F, const error_type & > >()
+            : make_unexpected( detail::invoke( std::forward< F >( f ), error() ) );
+    }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+    template<typename F
+        nsel_REQUIRES_T(
+            detail::valid_unexpected_type< detail::transform_invoke_result_t< F, error_type && > >::value
+        )
+    >
+    nsel_constexpr14 expected< void, detail::transform_invoke_result_t< F, error_type && > > transform_error( F && f ) &&
+    {
+        return has_value()
+            ? expected< void, detail::transform_invoke_result_t< F, error_type && > >()
+            : make_unexpected( detail::invoke( std::forward< F >( f ), std::move( error() ) ) );
+    }
+
+    template<typename F
+        nsel_REQUIRES_T(
+            detail::valid_unexpected_type< detail::transform_invoke_result_t< F, const error_type && > >::value
+        )
+    >
+    nsel_constexpr expected< void, detail::transform_invoke_result_t< F, const error_type && > > transform_error( F && f ) const &&
+    {
+        return has_value()
+            ? expected< void, detail::transform_invoke_result_t< F, const error_type && > >()
+            : make_unexpected( detail::invoke( std::forward< F >( f ), std::move( error() ) ) );
+    }
+#endif
+#endif // nsel_P2505R >= 3
+
+//  template constexpr 'see below' unwrap() const&;
+//
+//  template 'see below' unwrap() &&;
+
+    // factories
+
+//  template< typename Ex, typename F>
+//  expected<void,E> catch_exception(F&& f);
+//
+//  template< typename F>
+//  expected<decltype(func()), E> map(F&& func) ;
+//
+//  template< typename F>
+//  'see below' bind(F&& func) ;
+//
+//  template< typename F>
+//  expected<void,E> catch_error(F&& f);
+//
+//  template< typename F>
+//  'see below' then(F&& func);
+
+private:
+    detail::storage_t
+    <
+        void
+        , E
+        , std::is_copy_constructible<E>::value
+        , std::is_move_constructible<E>::value
+    >
+    contained;
+};
+
+// x.x.4.6 expected<>: comparison operators
+
+template< typename T1, typename E1, typename T2, typename E2
+    nsel_REQUIRES_T(
+        !std::is_void<T1>::value && !std::is_void<T2>::value
+    )
+>
+constexpr bool operator==( expected<T1,E1> const & x, expected<T2,E2> const & y )
+{
+    return bool(x) != bool(y) ? false : bool(x) ? *x == *y : x.error() == y.error();
+}
+
+template< typename T1, typename E1, typename T2, typename E2
+    nsel_REQUIRES_T(
+        std::is_void<T1>::value && std::is_void<T2>::value
+    )
+>
+constexpr bool operator==( expected<T1,E1> const & x, expected<T2,E2> const & y )
+{
+    return bool(x) != bool(y) ? false : bool(x) || static_cast<bool>( x.error() == y.error() );
+}
+
+template< typename T1, typename E1, typename T2, typename E2 >
+constexpr bool operator!=( expected<T1,E1> const & x, expected<T2,E2> const & y )
+{
+    return !(x == y);
+}
+
+#if nsel_P0323R <= 2
+
+template< typename T, typename E >
+constexpr bool operator<( expected<T,E> const & x, expected<T,E> const & y )
+{
+    return (!y) ? false : (!x) ? true : *x < *y;
+}
+
+template< typename T, typename E >
+constexpr bool operator>( expected<T,E> const & x, expected<T,E> const & y )
+{
+    return (y < x);
+}
+
+template< typename T, typename E >
+constexpr bool operator<=( expected<T,E> const & x, expected<T,E> const & y )
+{
+    return !(y < x);
+}
+
+template< typename T, typename E >
+constexpr bool operator>=( expected<T,E> const & x, expected<T,E> const & y )
+{
+    return !(x < y);
+}
+
+#endif
+
+// x.x.4.7 expected: comparison with T
+
+template< typename T1, typename E1, typename T2
+    nsel_REQUIRES_T(
+        !std::is_void<T1>::value
+    )
+>
+constexpr bool operator==( expected<T1,E1> const & x, T2 const & v )
+{
+    return bool(x) ? *x == v : false;
+}
+
+template< typename T1, typename E1, typename T2
+    nsel_REQUIRES_T(
+        !std::is_void<T1>::value
+    )
+>
+constexpr bool operator==(T2 const & v, expected<T1,E1> const & x )
+{
+    return bool(x) ? v == *x : false;
+}
+
+template< typename T1, typename E1, typename T2 >
+constexpr bool operator!=( expected<T1,E1> const & x, T2 const & v )
+{
+    return bool(x) ? *x != v : true;
+}
+
+template< typename T1, typename E1, typename T2 >
+constexpr bool operator!=( T2 const & v, expected<T1,E1> const & x )
+{
+    return bool(x) ? v != *x : true;
+}
+
+#if nsel_P0323R <= 2
+
+template< typename T, typename E >
+constexpr bool operator<( expected<T,E> const & x, T const & v )
+{
+    return bool(x) ? *x < v : true;
+}
+
+template< typename T, typename E >
+constexpr bool operator<( T const & v, expected<T,E> const & x )
+{
+    return bool(x) ? v < *x : false;
+}
+
+template< typename T, typename E >
+constexpr bool operator>( T const & v, expected<T,E> const & x )
+{
+    return bool(x) ? *x < v : false;
+}
+
+template< typename T, typename E >
+constexpr bool operator>( expected<T,E> const & x, T const & v )
+{
+    return bool(x) ? v < *x : false;
+}
+
+template< typename T, typename E >
+constexpr bool operator<=( T const & v, expected<T,E> const & x )
+{
+    return bool(x) ? ! ( *x < v ) : false;
+}
+
+template< typename T, typename E >
+constexpr bool operator<=( expected<T,E> const & x, T const & v )
+{
+    return bool(x) ? ! ( v < *x ) : true;
+}
+
+template< typename T, typename E >
+constexpr bool operator>=( expected<T,E> const & x, T const & v )
+{
+    return bool(x) ? ! ( *x < v ) : false;
+}
+
+template< typename T, typename E >
+constexpr bool operator>=( T const & v, expected<T,E> const & x )
+{
+    return bool(x) ? ! ( v < *x ) : true;
+}
+
+#endif // nsel_P0323R
+
+// x.x.4.8 expected: comparison with unexpected_type
+
+template< typename T1, typename E1 , typename E2 >
+constexpr bool operator==( expected<T1,E1> const & x, unexpected_type<E2> const & u )
+{
+    return (!x) ? x.get_unexpected() == u : false;
+}
+
+template< typename T1, typename E1 , typename E2 >
+constexpr bool operator==( unexpected_type<E2> const & u, expected<T1,E1> const & x )
+{
+    return ( x == u );
+}
+
+template< typename T1, typename E1 , typename E2 >
+constexpr bool operator!=( expected<T1,E1> const & x, unexpected_type<E2> const & u )
+{
+    return ! ( x == u );
+}
+
+template< typename T1, typename E1 , typename E2 >
+constexpr bool operator!=( unexpected_type<E2> const & u, expected<T1,E1> const & x )
+{
+    return ! ( x == u );
+}
+
+#if nsel_P0323R <= 2
+
+template< typename T, typename E >
+constexpr bool operator<( expected<T,E> const & x, unexpected_type<E> const & u )
+{
+    return (!x) ? ( x.get_unexpected() < u ) : false;
+}
+
+template< typename T, typename E >
+constexpr bool operator<( unexpected_type<E> const & u, expected<T,E> const & x )
+{
+  return (!x) ? ( u < x.get_unexpected() ) : true ;
+}
+
+template< typename T, typename E >
+constexpr bool operator>( expected<T,E> const & x, unexpected_type<E> const & u )
+{
+    return ( u < x );
+}
+
+template< typename T, typename E >
+constexpr bool operator>( unexpected_type<E> const & u, expected<T,E> const & x )
+{
+    return ( x < u );
+}
+
+template< typename T, typename E >
+constexpr bool operator<=( expected<T,E> const & x, unexpected_type<E> const & u )
+{
+    return ! ( u < x );
+}
+
+template< typename T, typename E >
+constexpr bool operator<=( unexpected_type<E> const & u, expected<T,E> const & x)
+{
+    return ! ( x < u );
+}
+
+template< typename T, typename E >
+constexpr bool operator>=( expected<T,E> const & x, unexpected_type<E> const & u  )
+{
+    return ! ( u > x );
+}
+
+template< typename T, typename E >
+constexpr bool operator>=( unexpected_type<E> const & u, expected<T,E> const & x )
+{
+    return ! ( x > u );
+}
+
+#endif // nsel_P0323R
+
+/// x.x.x Specialized algorithms
+
+template< typename T, typename E
+    nsel_REQUIRES_T(
+        ( std::is_void<T>::value || std::is_move_constructible<T>::value )
+        && std::is_move_constructible<E>::value
+        && std17::is_swappable<T>::value
+        && std17::is_swappable<E>::value )
+>
+void swap( expected<T,E> & x, expected<T,E> & y ) noexcept ( noexcept ( x.swap(y) ) )
+{
+    x.swap( y );
+}
+
+#if nsel_P0323R <= 3
+
+template< typename T >
+constexpr auto make_expected( T && v ) -> expected< typename std::decay<T>::type >
+{
+    return expected< typename std::decay<T>::type >( std::forward<T>( v ) );
+}
+
+// expected<void> specialization:
+
+auto inline make_expected() -> expected<void>
+{
+    return expected<void>( in_place );
+}
+
+template< typename T >
+constexpr auto make_expected_from_current_exception() -> expected<T>
+{
+    return expected<T>( make_unexpected_from_current_exception() );
+}
+
+template< typename T >
+auto make_expected_from_exception( std::exception_ptr v ) -> expected<T>
+{
+    return expected<T>( unexpected_type<std::exception_ptr>( std::forward<std::exception_ptr>( v ) ) );
+}
+
+template< typename T, typename E >
+constexpr auto make_expected_from_error( E e ) -> expected<T, typename std::decay<E>::type>
+{
+    return expected<T, typename std::decay<E>::type>( make_unexpected( e ) );
+}
+
+template< typename F
+    nsel_REQUIRES_T( ! std::is_same<typename std::result_of<F()>::type, void>::value )
+>
+/*nsel_constexpr14*/
+auto make_expected_from_call( F f ) -> expected< typename std::result_of<F()>::type >
+{
+    try
+    {
+        return make_expected( f() );
+    }
+    catch (...)
+    {
+        return make_unexpected_from_current_exception();
+    }
+}
+
+template< typename F
+    nsel_REQUIRES_T( std::is_same<typename std::result_of<F()>::type, void>::value )
+>
+/*nsel_constexpr14*/
+auto make_expected_from_call( F f ) -> expected<void>
+{
+    try
+    {
+        f();
+        return make_expected();
+    }
+    catch (...)
+    {
+        return make_unexpected_from_current_exception();
+    }
+}
+
+#endif // nsel_P0323R
+
+} // namespace expected_lite
+
+using namespace expected_lite;
+
+// using expected_lite::expected;
+// using ...
+
+} // namespace future_std
+
+namespace std {
+
+// expected: hash support
+
+template< typename T, typename E >
+struct hash< future_std::expected<T,E> >
+{
+    using result_type = std::size_t;
+    using argument_type = future_std::expected<T,E>;
+
+    constexpr result_type operator()(argument_type const & arg) const
+    {
+        return arg ? std::hash<T>{}(*arg) : result_type{};
+    }
+};
+
+// TBD - ?? remove? see spec.
+template< typename T, typename E >
+struct hash< future_std::expected<T&,E> >
+{
+    using result_type = std::size_t;
+    using argument_type = future_std::expected<T&,E>;
+
+    constexpr result_type operator()(argument_type const & arg) const
+    {
+        return arg ? std::hash<T>{}(*arg) : result_type{};
+    }
+};
+
+// TBD - implement
+// bool(e), hash<expected<void,E>>()(e) shall evaluate to the hashing true;
+// otherwise it evaluates to an unspecified value if E is exception_ptr or
+// a combination of hashing false and hash<E>()(e.error()).
+
+template< typename E >
+struct hash< future_std::expected<void,E> >
+{
+};
+
+} // namespace std
+
+namespace future_std {
+
+// void unexpected() is deprecated && removed in C++17
+
+#if nsel_CPP17_OR_GREATER || nsel_COMPILER_MSVC_VERSION > 141
+template< typename E >
+using unexpected = unexpected_type<E>;
+#endif
+
+} // namespace future_std
+
+#undef nsel_REQUIRES
+#undef nsel_REQUIRES_0
+#undef nsel_REQUIRES_T
+
+nsel_RESTORE_WARNINGS()
+
+#endif // nsel_USES_STD_EXPECTED
+
+#endif // AIDGE_CORE_UTILS_FUTURE_STD_EXPECTED_H_
diff --git a/python_binding/operator/pybind_AvgPooling.cpp b/python_binding/operator/pybind_AvgPooling.cpp
index a2eda1ab44f75b2f6a63d0d4d4f19cbee00b07c7..fe67fcb7a26f6ea1f05577b47444df5cb271110a 100644
--- a/python_binding/operator/pybind_AvgPooling.cpp
+++ b/python_binding/operator/pybind_AvgPooling.cpp
@@ -8,7 +8,7 @@
  * SPDX-License-Identifier: EPL-2.0
  *
  ********************************************************************************/
-#ifdef PYBIND
+
 #include <pybind11/pybind11.h>
 #include <pybind11/stl.h>
 
@@ -39,25 +39,10 @@ template <DimIdx_t DIM> void declare_AvgPoolingOp(py::module &m) {
   m.def(("AvgPooling" + std::to_string(DIM) + "D").c_str(), [](const std::vector<DimSize_t>& kernel_dims,
                                                                   const std::string& name,
                                                                   const std::vector<DimSize_t> &stride_dims) {
-        // Lambda function wrapper because PyBind fails to convert const array.
-        // So we use a vector that we convert in this function to a const DimeSize_t [DIM] array.
-        if (kernel_dims.size() != DIM) {
-            throw std::runtime_error("kernel_dims size [" + std::to_string(kernel_dims.size()) + "] does not match DIM [" + std::to_string(DIM) +"]");
-        }
-        if (stride_dims.size() != DIM) {
-            throw std::runtime_error("stride_dims size [" + std::to_string(stride_dims.size()) + "] does not match DIM [" + std::to_string(DIM) +"]");
-        }
-        DimSize_t tmp_kernel_dims_array[DIM];
-        for (size_t i = 0; i < DIM; ++i) {
-            tmp_kernel_dims_array[i] = kernel_dims[i];
-        }
-        DimSize_t tmp_stride_dims_array[DIM];
-        for (size_t i = 0; i < DIM; ++i) {
-            tmp_stride_dims_array[i] = stride_dims[i];
-        }
-        const DimSize_t (&kernel_dims_array)[DIM] = tmp_kernel_dims_array;
-        const DimSize_t (&stride_dims_array)[DIM] = tmp_stride_dims_array;
-        return AvgPooling<DIM>(to_array(kernel_dims_array), name, to_array(stride_dims_array));
+        AIDGE_ASSERT(kernel_dims.size() == DIM, "kernel_dims size [%ld] does not match DIM [%d]", kernel_dims.size(), DIM);
+        AIDGE_ASSERT(stride_dims.size() == DIM, "stride_dims size [%ld] does not match DIM [%d]", stride_dims.size(), DIM);
+
+        return AvgPooling<DIM>(to_array<DIM>(kernel_dims.begin()), name, to_array<DIM>(stride_dims.begin()));
     }, py::arg("kernel_dims"),
        py::arg("name") = "",
        py::arg("stride_dims") = std::vector<DimSize_t>(DIM,1));
@@ -75,4 +60,3 @@ void init_AvgPooling(py::module &m) {
   // (&)[1])>(&AvgPooling));
 }
 } // namespace Aidge
-#endif
diff --git a/python_binding/operator/pybind_Conv.cpp b/python_binding/operator/pybind_Conv.cpp
index aabbfd3dd9aecd44123962443458de56c0b7071c..3801fac8a8ca8461fe6ec74cf75313fc362d15d4 100644
--- a/python_binding/operator/pybind_Conv.cpp
+++ b/python_binding/operator/pybind_Conv.cpp
@@ -48,33 +48,11 @@ template <DimIdx_t DIM> void declare_ConvOp(py::module &m) {
                                                          const std::string& name,
                                                          const std::vector<DimSize_t> &stride_dims,
                                                          const std::vector<DimSize_t> &dilation_dims) {
-        // Lambda function wrapper because PyBind fails to convert const array.
-        // So we use a vector that we convert in this function to a const DimeSize_t [DIM] array.
-        if (kernel_dims.size() != DIM) {
-            throw std::runtime_error("kernel_dims size [" + std::to_string(kernel_dims.size()) + "] does not match DIM [" + std::to_string(DIM) +"]");
-        }
-        if (stride_dims.size() != DIM) {
-            throw std::runtime_error("stride_dims size [" + std::to_string(stride_dims.size()) + "] does not match DIM [" + std::to_string(DIM) +"]");
-        }
-        if (dilation_dims.size() != DIM) {
-            throw std::runtime_error("dilation_dims size [" + std::to_string(dilation_dims.size()) + "] does not match DIM [" + std::to_string(DIM) +"]");
-        }
-        DimSize_t tmp_kernel_dims_array[DIM];
-        for (size_t i = 0; i < DIM; ++i) {
-            tmp_kernel_dims_array[i] = kernel_dims[i];
-        }
-        DimSize_t tmp_stride_dims_array[DIM];
-        for (size_t i = 0; i < DIM; ++i) {
-            tmp_stride_dims_array[i] = stride_dims[i];
-        }
-        DimSize_t tmp_dilation_dims_array[DIM];
-        for (size_t i = 0; i < DIM; ++i) {
-            tmp_dilation_dims_array[i] = dilation_dims[i];
-        }
-        const DimSize_t (&kernel_dims_array)[DIM] = tmp_kernel_dims_array;
-        const DimSize_t (&stride_dims_array)[DIM] = tmp_stride_dims_array;
-        const DimSize_t (&dilation_dims_array)[DIM] = tmp_dilation_dims_array;
-        return Conv<DIM>(in_channels, out_channels, to_array(kernel_dims_array), name, to_array(stride_dims_array), to_array(dilation_dims_array));
+        AIDGE_ASSERT(kernel_dims.size() == DIM, "kernel_dims size [%ld] does not match DIM [%d]", kernel_dims.size(), DIM);
+        AIDGE_ASSERT(stride_dims.size() == DIM, "stride_dims size [%ld] does not match DIM [%d]", stride_dims.size(), DIM);
+        AIDGE_ASSERT(dilation_dims.size() == DIM, "dilation_dims size [%ld] does not match DIM [%d]", dilation_dims.size(), DIM);
+
+        return Conv<DIM>(in_channels, out_channels, to_array<DIM>(kernel_dims.begin()), name, to_array<DIM>(stride_dims.begin()), to_array<DIM>(dilation_dims.begin()));
     }, py::arg("in_channels"),
        py::arg("out_channels"),
        py::arg("kernel_dims"),
diff --git a/python_binding/operator/pybind_ConvDepthWise.cpp b/python_binding/operator/pybind_ConvDepthWise.cpp
index 809a7d6e797651ed8c490aa9a886c31c7e4e6651..4745ef345264763f1a890d566235be072c8e50d8 100644
--- a/python_binding/operator/pybind_ConvDepthWise.cpp
+++ b/python_binding/operator/pybind_ConvDepthWise.cpp
@@ -42,33 +42,11 @@ template <DimIdx_t DIM> void declare_ConvDepthWiseOp(py::module &m) {
                                                                   const std::string& name,
                                                                   const std::vector<DimSize_t> &stride_dims,
                                                                   const std::vector<DimSize_t> &dilation_dims) {
-        // Lambda function wrapper because PyBind fails to convert const array.
-        // So we use a vector that we convert in this function to a const DimeSize_t [DIM] array.
-        if (kernel_dims.size() != DIM) {
-            throw std::runtime_error("kernel_dims size [" + std::to_string(kernel_dims.size()) + "] does not match DIM [" + std::to_string(DIM) +"]");
-        }
-        if (stride_dims.size() != DIM) {
-            throw std::runtime_error("stride_dims size [" + std::to_string(stride_dims.size()) + "] does not match DIM [" + std::to_string(DIM) +"]");
-        }
-        if (dilation_dims.size() != DIM) {
-            throw std::runtime_error("dilation_dims size [" + std::to_string(dilation_dims.size()) + "] does not match DIM [" + std::to_string(DIM) +"]");
-        }
-        DimSize_t tmp_kernel_dims_array[DIM];
-        for (size_t i = 0; i < DIM; ++i) {
-            tmp_kernel_dims_array[i] = kernel_dims[i];
-        }
-        DimSize_t tmp_stride_dims_array[DIM];
-        for (size_t i = 0; i < DIM; ++i) {
-            tmp_stride_dims_array[i] = stride_dims[i];
-        }
-        DimSize_t tmp_dilation_dims_array[DIM];
-        for (size_t i = 0; i < DIM; ++i) {
-            tmp_dilation_dims_array[i] = dilation_dims[i];
-        }
-        const DimSize_t (&kernel_dims_array)[DIM] = tmp_kernel_dims_array;
-        const DimSize_t (&stride_dims_array)[DIM] = tmp_stride_dims_array;
-        const DimSize_t (&dilation_dims_array)[DIM] = tmp_dilation_dims_array;
-        return ConvDepthWise<DIM>(to_array(kernel_dims_array), name, to_array(stride_dims_array), to_array(dilation_dims_array));
+        AIDGE_ASSERT(kernel_dims.size() == DIM, "kernel_dims size [%ld] does not match DIM [%d]", kernel_dims.size(), DIM);
+        AIDGE_ASSERT(stride_dims.size() == DIM, "stride_dims size [%ld] does not match DIM [%d]", stride_dims.size(), DIM);
+        AIDGE_ASSERT(dilation_dims.size() == DIM, "dilation_dims size [%ld] does not match DIM [%d]", dilation_dims.size(), DIM);
+
+        return ConvDepthWise<DIM>(to_array<DIM>(kernel_dims.begin()), name, to_array<DIM>(stride_dims.begin()), to_array<DIM>(dilation_dims.begin()));
     }, py::arg("kernel_dims"),
        py::arg("name") = "",
        py::arg("stride_dims") = std::vector<DimSize_t>(DIM,1),
diff --git a/python_binding/operator/pybind_MaxPooling.cpp b/python_binding/operator/pybind_MaxPooling.cpp
index 84313f298f90298726773630602e90a5ab3d3efd..c83dfaa3639f05af345bd9214460f95fd661cd31 100644
--- a/python_binding/operator/pybind_MaxPooling.cpp
+++ b/python_binding/operator/pybind_MaxPooling.cpp
@@ -8,7 +8,7 @@
  * SPDX-License-Identifier: EPL-2.0
  *
  ********************************************************************************/
-#ifdef PYBIND
+
 #include <pybind11/pybind11.h>
 #include <pybind11/stl.h>
 
@@ -39,25 +39,10 @@ template <DimIdx_t DIM> void declare_MaxPoolingOp(py::module &m) {
   m.def(("MaxPooling" + std::to_string(DIM) + "D").c_str(), [](const std::vector<DimSize_t>& kernel_dims,
                                                                   const std::string& name,
                                                                   const std::vector<DimSize_t> &stride_dims) {
-        // Lambda function wrapper because PyBind fails to convert const array.
-        // So we use a vector that we convert in this function to a const DimeSize_t [DIM] array.
-        if (kernel_dims.size() != DIM) {
-            throw std::runtime_error("kernel_dims size [" + std::to_string(kernel_dims.size()) + "] does not match DIM [" + std::to_string(DIM) +"]");
-        }
-        if (stride_dims.size() != DIM) {
-            throw std::runtime_error("stride_dims size [" + std::to_string(stride_dims.size()) + "] does not match DIM [" + std::to_string(DIM) +"]");
-        }
-        DimSize_t tmp_kernel_dims_array[DIM];
-        for (size_t i = 0; i < DIM; ++i) {
-            tmp_kernel_dims_array[i] = kernel_dims[i];
-        }
-        DimSize_t tmp_stride_dims_array[DIM];
-        for (size_t i = 0; i < DIM; ++i) {
-            tmp_stride_dims_array[i] = stride_dims[i];
-        }
-        const DimSize_t (&kernel_dims_array)[DIM] = tmp_kernel_dims_array;
-        const DimSize_t (&stride_dims_array)[DIM] = tmp_stride_dims_array;
-        return MaxPooling<DIM>(to_array(kernel_dims_array), name, to_array(stride_dims_array));
+        AIDGE_ASSERT(kernel_dims.size() == DIM, "kernel_dims size [%ld] does not match DIM [%d]", kernel_dims.size(), DIM);
+        AIDGE_ASSERT(stride_dims.size() == DIM, "stride_dims size [%ld] does not match DIM [%d]", stride_dims.size(), DIM);
+
+        return MaxPooling<DIM>(to_array<DIM>(kernel_dims.begin()), name, to_array<DIM>(stride_dims.begin()));
     }, py::arg("kernel_dims"),
        py::arg("name") = "",
        py::arg("stride_dims") = std::vector<DimSize_t>(DIM,1));
@@ -75,4 +60,3 @@ void init_MaxPooling(py::module &m) {
   // (&)[1])>(&MaxPooling));
 }
 } // namespace Aidge
-#endif
diff --git a/python_binding/operator/pybind_MetaOperatorDefs.cpp b/python_binding/operator/pybind_MetaOperatorDefs.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3372d50e14be9e0d24ba5d9171766255ab49f23b
--- /dev/null
+++ b/python_binding/operator/pybind_MetaOperatorDefs.cpp
@@ -0,0 +1,126 @@
+/********************************************************************************
+ * Copyright (c) 2023 CEA-List
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ********************************************************************************/
+
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+
+#include <string>
+#include <vector>
+#include <array>
+
+#include "aidge/backend/OperatorImpl.hpp"
+#include "aidge/operator/MetaOperatorDefs.hpp"
+#include "aidge/operator/Operator.hpp"
+#include "aidge/utils/Types.h"
+
+namespace py = pybind11;
+namespace Aidge {
+
+template <DimIdx_t DIM> void declare_PaddedConvOp(py::module &m) {
+  m.def(("PaddedConv" + std::to_string(DIM) + "D").c_str(), [](DimSize_t in_channels,
+                                                         DimSize_t out_channels,
+                                                         const std::vector<DimSize_t>& kernel_dims,
+                                                         const std::string& name, 
+                                                         const std::vector<DimSize_t> &stride_dims,
+                                                         const std::vector<DimSize_t> &padding_dims,
+                                                         const std::vector<DimSize_t> &dilation_dims)
+    {
+        AIDGE_ASSERT(kernel_dims.size() == DIM, "kernel_dims size [%ld] does not match DIM [%d]", kernel_dims.size(), DIM);
+        AIDGE_ASSERT(stride_dims.size() == DIM, "stride_dims size [%ld] does not match DIM [%d]", stride_dims.size(), DIM);
+        AIDGE_ASSERT(padding_dims.size() == 2*DIM, "padding_dims size [%ld] does not match DIM [%d]", padding_dims.size(), 2*DIM);
+        AIDGE_ASSERT(dilation_dims.size() == DIM, "dilation_dims size [%ld] does not match DIM [%d]", dilation_dims.size(), DIM);
+
+        return PaddedConv<DIM>(in_channels, out_channels, to_array<DIM>(kernel_dims.begin()), name, to_array<DIM>(stride_dims.begin()), to_array<2*DIM>(padding_dims.begin()), to_array<DIM>(dilation_dims.begin()));
+    }, py::arg("in_channels"),
+       py::arg("out_channels"),
+       py::arg("kernel_dims"),
+       py::arg("name") = "",
+       py::arg("stride_dims") = std::vector<DimSize_t>(DIM,1),
+       py::arg("padding_dims") = std::vector<DimSize_t>(2*DIM,0),
+       py::arg("dilation_dims") = std::vector<DimSize_t>(DIM,1));
+}
+
+template <DimIdx_t DIM> void declare_PaddedConvDepthWiseOp(py::module &m) {
+  m.def(("PaddedConvDepthWise" + std::to_string(DIM) + "D").c_str(), [](const std::vector<DimSize_t>& kernel_dims,
+                                                         const std::string& name, 
+                                                         const std::vector<DimSize_t> &stride_dims,
+                                                         const std::vector<DimSize_t> &padding_dims,
+                                                         const std::vector<DimSize_t> &dilation_dims)
+    {
+        AIDGE_ASSERT(kernel_dims.size() == DIM, "kernel_dims size [%ld] does not match DIM [%d]", kernel_dims.size(), DIM);
+        AIDGE_ASSERT(stride_dims.size() == DIM, "stride_dims size [%ld] does not match DIM [%d]", stride_dims.size(), DIM);
+        AIDGE_ASSERT(padding_dims.size() == 2*DIM, "padding_dims size [%ld] does not match DIM [%d]", padding_dims.size(), 2*DIM);
+        AIDGE_ASSERT(dilation_dims.size() == DIM, "dilation_dims size [%ld] does not match DIM [%d]", dilation_dims.size(), DIM);
+
+        return PaddedConvDepthWise<DIM>(to_array<DIM>(kernel_dims.begin()), name, to_array<DIM>(stride_dims.begin()), to_array<2*DIM>(padding_dims.begin()), to_array<DIM>(dilation_dims.begin()));
+    }, py::arg("kernel_dims"),
+       py::arg("name") = "",
+       py::arg("stride_dims") = std::vector<DimSize_t>(DIM,1),
+       py::arg("padding_dims") = std::vector<DimSize_t>(2*DIM,0),
+       py::arg("dilation_dims") = std::vector<DimSize_t>(DIM,1));
+  
+}
+
+template <DimIdx_t DIM> void declare_PaddedAvgPoolingOp(py::module &m) {
+  m.def(("PaddedAvgPooling" + std::to_string(DIM) + "D").c_str(), [](const std::vector<DimSize_t>& kernel_dims,
+                                                         const std::string& name, 
+                                                         const std::vector<DimSize_t> &stride_dims,
+                                                         const std::vector<DimSize_t> &padding_dims)
+    {
+        AIDGE_ASSERT(kernel_dims.size() == DIM, "kernel_dims size [%ld] does not match DIM [%d]", kernel_dims.size(), DIM);
+        AIDGE_ASSERT(stride_dims.size() == DIM, "stride_dims size [%ld] does not match DIM [%d]", stride_dims.size(), DIM);
+        AIDGE_ASSERT(padding_dims.size() == 2*DIM, "padding_dims size [%ld] does not match DIM [%d]", padding_dims.size(), 2*DIM);
+
+        return PaddedAvgPooling<DIM>(to_array<DIM>(kernel_dims.begin()), name, to_array<DIM>(stride_dims.begin()), to_array<2*DIM>(padding_dims.begin()));
+    }, py::arg("kernel_dims"),
+       py::arg("name") = "",
+       py::arg("stride_dims") = std::vector<DimSize_t>(DIM,1),
+       py::arg("padding_dims") = std::vector<DimSize_t>(2*DIM,0));
+  
+}
+
+template <DimIdx_t DIM> void declare_PaddedMaxPoolingOp(py::module &m) {
+  m.def(("PaddedMaxPooling" + std::to_string(DIM) + "D").c_str(), [](const std::vector<DimSize_t>& kernel_dims,
+                                                         const std::string& name, 
+                                                         const std::vector<DimSize_t> &stride_dims,
+                                                         const std::vector<DimSize_t> &padding_dims)
+    {
+        AIDGE_ASSERT(kernel_dims.size() == DIM, "kernel_dims size [%ld] does not match DIM [%d]", kernel_dims.size(), DIM);
+        AIDGE_ASSERT(stride_dims.size() == DIM, "stride_dims size [%ld] does not match DIM [%d]", stride_dims.size(), DIM);
+        AIDGE_ASSERT(padding_dims.size() == 2*DIM, "padding_dims size [%ld] does not match DIM [%d]", padding_dims.size(), 2*DIM);
+
+        return PaddedMaxPooling<DIM>(to_array<DIM>(kernel_dims.begin()), name, to_array<DIM>(stride_dims.begin()), to_array<2*DIM>(padding_dims.begin()));
+    }, py::arg("kernel_dims"),
+       py::arg("name") = "",
+       py::arg("stride_dims") = std::vector<DimSize_t>(DIM,1),
+       py::arg("padding_dims") = std::vector<DimSize_t>(2*DIM,0));
+  
+}
+
+void init_MetaOperatorDefs(py::module &m) {
+  declare_PaddedConvOp<1>(m);
+  declare_PaddedConvOp<2>(m);
+  declare_PaddedConvOp<3>(m);
+  declare_PaddedConvDepthWiseOp<1>(m);
+  declare_PaddedConvDepthWiseOp<2>(m);
+  declare_PaddedConvDepthWiseOp<3>(m);
+  declare_PaddedAvgPoolingOp<1>(m);
+  declare_PaddedAvgPoolingOp<2>(m);
+  declare_PaddedAvgPoolingOp<3>(m);
+  declare_PaddedMaxPoolingOp<1>(m);
+  declare_PaddedMaxPoolingOp<2>(m);
+  declare_PaddedMaxPoolingOp<3>(m);
+ 
+  // FIXME:
+  // m.def("Conv1D", static_cast<NodeAPI(*)(const char*, int, int, int const
+  // (&)[1])>(&Conv));
+}
+} // namespace Aidge
diff --git a/python_binding/pybind_core.cpp b/python_binding/pybind_core.cpp
index d1287c0a928ae2ad27a839cec1c3d3955da65538..e9777a220f7dc8d491a8cd8220f3d99f673a8e8d 100644
--- a/python_binding/pybind_core.cpp
+++ b/python_binding/pybind_core.cpp
@@ -30,6 +30,7 @@ void init_GenericOperator(py::module&);
 void init_LeakyReLU(py::module&);
 void init_MatMul(py::module&);
 void init_MaxPooling(py::module&);
+void init_MetaOperatorDefs(py::module&);
 void init_Producer(py::module&);
 void init_ReLU(py::module&);
 void init_Softmax(py::module&);
diff --git a/unit_tests/operator/Test_MetaOperator.cpp b/unit_tests/operator/Test_MetaOperator.cpp
index e9718fc694d29713797565d3ae8c8107cc7612de..c090427914390369452ce3259f47830f01ab1754 100644
--- a/unit_tests/operator/Test_MetaOperator.cpp
+++ b/unit_tests/operator/Test_MetaOperator.cpp
@@ -20,7 +20,7 @@ using namespace Aidge;
 
 TEST_CASE("[core/operators] MetaOperator", "[Operator]") {
     SECTION("PaddedConv") {
-        auto op = PaddedConv(1, 3, {3, 3}, "padded_conv", {1, 1}, {{{1, 1}, {1, 1}}});
+        auto op = PaddedConv(1, 3, {3, 3}, "padded_conv", {1, 1}, {1, 1, 1, 1});
 
         auto microGraph = std::dynamic_pointer_cast<MetaOperator_Op>(op->getOperator())->getMicroGraph();