Search code examples
c++pointersvariant

Perform operations on std::variant<POD_PTR*...> in a generic way


Say the following variant is given:
using Variant = std::variant<uint32_t*, uint16_t*>

And then, a vector of such variants is needed:
using VariantVecType = std::vector<Variant>

How would one go about accessing the underlying values (pointed to by those pointers) to perform arithmetic operations on them in a more generic way (i.e. without using std::get_if) e.g.

   uint32_t i0 = 1;
   uint16_t i1 = 2;

   Variant v0{ &i0 };
   Variant v1{ &i1 };

   VariantVecType vec{v0, v1};

   for(auto& v : vec)
        if(auto pval = std::get_if<uint32_t*>(&v))
            std::cout << "Underlying int value: " << *(*pval) << '\n';  
        else if(auto pval = std::get_if<uint16_t*>(&v))
            std::cout << "Underlying int value: " << *(*pval) << '\n';  
        else
            std::cout << "failed to get value!" << '\n';

Solution

  • Usually you would use std::visit

    example

    #include <iostream>
    #include <variant>
    #include <cstdint>
    #include <vector>
    
    // from cppref:
    // helper type for the visitor #4
    template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
    // explicit deduction guide (not needed as of C++20)
    template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
     
    
    int main() {
        uint32_t i0 = 1;
        uint16_t i1 = 2;
    
        using Variant = std::variant<uint32_t*, uint16_t*>;
        Variant v0{ &i0 };
        Variant v1{ &i1 };
    
        using VariantVecType = std::vector<Variant>;
        VariantVecType vec{v0, v1};
    
       for(auto& v : vec) {
           std::visit(overloaded {
                [](uint32_t* arg) { std::cout << "Underlying uint32 value: " << *arg << '\n'; },
                [](uint16_t* arg) { std::cout << "Underlying uint16 value: " << *arg << '\n'; },
           }, v);
       }
    }
    

    But a std::variant<uint32_t*, uint16_t*> seems a bit awkward. pointers in 64-bit machines are bigger then both types. And accessing the elements requires indirection=slow. Plus the arithmetic on both types will be about the same (except for overflow). So why not have a normal std::vector<uint32_t>?