Home > Enterprise >  Does the standard or some Boost library offer a compile-time variant?
Does the standard or some Boost library offer a compile-time variant?

Time:06-29

I'm not asking how to Get type of a std::variant member at a given index during compile time.

I'm asking whether the standard or some boost library offer a std::variant-like class whose contained type is known at compile time.

What I'm asking, I believe, is not completely nonsense, as there's boost::hana::optional which plays the same role (as the type I'm imagining), but with respect to std::optional.

As regards its utility, it would allow one to write several overloads into one, e.g.

void f(std::string, A);
void f(std::string, B);
void f(int, A);
void f(int, B);

could be written as

void f(var<std::string, int>, var<A, B>);

Clearly the implementation would make use of if constexpr to branch on the input types.

CodePudding user response:

You can already accomplish your stated goal with a function template. Any type of variant would complicate things unnecessarily. You would have a traits type somewhere in a header:

// one_of.h
#include <type_traits>

template <class T, class First, class...Rest>
struct one_of
{
   static_assert(std::is_same_v<std::remove_cvref_t<First>, First>);
   static constexpr bool value = std::is_same_v<std::remove_cvref_t<T>, First> 
                               || one_of<T, Rest...>::value;
};


template <class T, class First>
struct one_of<T, First>
{
   static_assert(std::is_same_v<std::remove_cvref_t<First>, First>);
   static constexpr bool value = std::is_same_v<std::remove_cvref_t<T>, First>;
};


template <class T, class...Cands>
constexpr bool one_of_v = one_of<T, Cands...>::value;

And then, in your actual program code, you would use the one_of_v template to restrict (via a c 20 function constraint) the types allowed as parameters:

#include "one_of.h"
#include <string>

struct A{};
struct B{};

template <class A1, class A2>
requires one_of_v<A1, std::string, int> && one_of_v<A2, A, B>
void f(A1 && a1, A2 && a2)
{
   if constexpr (std::is_same_v<int, A1>)
   {
       // do int stuff
   }
   else
   {
      // guaranteed to be string, do string stuff
   }
}

int main()
{
    int d;
    A e;
    f(d, e); // matches
    std::string s;
    B b;
    f(s, b); // matches
    //double a;
    //double b;
    //f(a, b); // no match
}

Now, one_of is a bit ham-handed and I would probably use more subtle traits types in real code, but this demonstrates the concept correctly.

I've personally found if constexpr with type traits very useful in reducing the number of overloads needed for template functions. Plus, it explains all the different cases more succinctly, all right next to each other, than overloaded functions that may be spread out in a way that you can't comprehend all at once.

CodePudding user response:

Building on the comments and Spencer's answer, C 20 concepts and abbreviated template functions will give you almost exactly what you described.

#include <type_traits>
#include <string>

template <class T, class... Ts>
concept var = (std::is_same_v<T, Ts> || ...);

struct A{};
struct B{};

void f(var<int, std::string> auto a1, var<A, B> auto a2) {
    if constexpr (std::is_same_v<decltype(a1), int>) {
        // int path
    } else {
        // string path
    }
}

https://godbolt.org/z/G5WdMTdGP

  • Related