Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::optional as template non-type parameter in C++?

I want to have non-type optional template parameters in my class/struct.

Of cause I can do:

template <bool has_arg0, int arg0>
struct S {};

and use S<true, 123> if there is value for arg0 and S<false, 0> if there is no value.

But would be nice to have just one template parameter instead of two.

For that first I tried to use std::optional:

Try it online!

#include <optional>

template <std::optional<size_t> X>
struct S {};

int main() {}

but above code doesn't compile with error error: 'std::optional<long unsigned int>' is not a valid type for a template non-type parameter because it is not structural. Looks like std::optional is not suitable for template non-type parameters.

Then I tried to implement my own solution:

Try it online!

#include <iostream>
#include <optional>

template <typename T>
struct Optional {
    constexpr Optional() = delete;
    constexpr Optional(nullptr_t) {}
    template <typename OT>
    constexpr Optional(OT const & val)
        : has_value(true), value(val) {}
    bool const has_value = false;
    T const value = T();
};

template <Optional<size_t> X = nullptr>
struct S {
    static constexpr auto x = X;
};

int main() {
    {
        S<123> s;
        std::cout << std::boolalpha << s.x.has_value << " " << s.x.value << std::endl;
    }
    {
        S<nullptr> s;
        std::cout << std::boolalpha << s.x.has_value << " " << s.x.value << std::endl;
    }
    {
        S s;
        std::cout << std::boolalpha << s.x.has_value << " " << s.x.value << std::endl;
    }
}

which compiles and works on recent compilers.

My question is whether there is some standard-library solution for this? Maybe there exists some std::optional-like class in standard library but suitable for template params, basically which does similar stuff as my Optional class in code above?

Also small accompanying question is what else besides nullptr I can use to initialize optional without value? Maybe there is some standard-library definition like struct null_type_t {}; which can be used everywhere to pass null-value?

like image 775
Arty Avatar asked Apr 23 '26 13:04

Arty


1 Answers

What about using variadic templates ?

#include <type_traits>
#include <iostream>

template <typename T, T...>
struct O;

template <typename T, T val>
struct O<T, val>
 {
   static constexpr bool has_value { true };

   using type = T;

   T value { val };
 };

template <typename T>
struct O<T>
 {
   static constexpr bool has_value { false };

   using type = T;
 };

template <typename...>
struct S;

template <char ... Cs, int ... Is, long ... Ls, long long ... Lls,
          bool ... Bs>
struct S<O<char, Cs...>, O<int, Is...>, O<long, Ls...>,
         O<long long, Lls...>, O<bool, Bs...>>
 {
   O<char,      Cs...>   v1;
   O<int,       Is...>   v2;
   O<long,      Ls...>   v3;
   O<long long, Lls...>  v4;
   O<bool,      Bs...>   v5;

 };

int main ()
 {
   S<O<char>, O<int, 123>, O<long>, O<long long>, O<bool, true>>  s;

   std::cout << s.v1.has_value << std::endl;
   std::cout << s.v2.has_value << ", " << s.v2.value << std::endl;
   std::cout << s.v3.has_value << std::endl;
   std::cout << s.v4.has_value << std::endl;
   std::cout << s.v5.has_value << ", " << s.v5.value << std::endl;
 }

If you can use C++17, you can simplify a little, using auto for the type of the value but maintaining the requirements abut the type of the optional for S (an optional char, an optional int, an optional long, an optional long long and an optional bool).

#include <iostream>

template <auto ...>
struct O;

template <auto val>
struct O<val>
 {
   static constexpr bool has_value { true };

   using type = decltype(val);

   type value { val };
 };

template <>
struct O<>
 { static constexpr bool has_value { false }; };

template <typename...>
struct S;

template <char ... Cs, int ... Is, long ... Ls, long long ... Lls,
          bool ... Bs>
struct S<O<Cs...>, O<Is...>, O<Ls...>, O<Lls...>, O<Bs...>>
 {
   O<Cs...>   v1;
   O<Is...>   v2;
   O<Ls...>   v3;
   O<Lls...>  v4;
   O<Bs...>   v5;
 };

int main ()
 {
   S<O<>, O<123>, O<>, O<>, O<true>>  s;

   std::cout << s.v1.has_value << std::endl;
   std::cout << s.v2.has_value << ", " << s.v2.value << std::endl;
   std::cout << s.v3.has_value << std::endl;
   std::cout << s.v4.has_value << std::endl;
   std::cout << s.v5.has_value << ", " << s.v5.value << std::endl;
 }
like image 68
max66 Avatar answered Apr 25 '26 04:04

max66



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!