Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
127 views
in Technique[技术] by (71.8m points)

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?

question from:https://stackoverflow.com/questions/65542144/stdoptional-as-template-non-type-parameter-in-c

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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;
 }

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...