Torisoft
Torisoft

Reputation: 31

Can a C++ function receive two types in one argument alternatively?

I wish to send an integer to a function, that integer might be 8/16 bits depending on conditions. Can I declare one function to receive either int8/in16 as argument, depending on what the caller sends? Or, do I have to declare two functions one for each type (what I am doing at present)?

void func(uint8_t/uint16_t value)

or

void func(uint8_t value) void func(uint16_t value)

Upvotes: 0

Views: 136

Answers (4)

Useless
Useless

Reputation: 67733

void func(uint8_t/uint16_t value)

The only way to do this exactly (without a function template instantiated as two functions) is with std::variant (or some other discriminated union).

void func(std::variant<uint8_t, uint16_t> value)
{
}

is precisely what you requested.

When you start implementing the body, you'll find it's often simpler to delegate to a templated visitor, and then you're back to the existing answers (but with a relatively expensive runtime decision about the type instead of a static one).

Demonstration in compiler explorer

void func(std::variant<uint8_t, uint16_t> value)
{
    std::cout << "called with type index " << value.index() << '\n';

    // templated visitor
    std::visit(
        [](auto && val)
        {
            std::cout << "called with type " << typeid(val).name()
             << ", value " << val << '\n';
        },
        value
        );
        
    // alternatively, test each type by hand
    if (std::holds_alternative<uint8_t>(value))
    {
        std::cout << "got uint8_t alternative " << std::get<uint8_t>(value)
        << '\n';
    }
    else if (std::holds_alternative<uint16_t>(value))
    {
        std::cout << "got uint16_t alternative " << std::get<uint16_t>(value)
        << '\n';
    }
}

Upvotes: 0

cptFracassa
cptFracassa

Reputation: 1022

@Jason Liam is onto it, but if you want to different functions, standard overloading is the answer. If you want to use a single function for those two types, combine Jason's functions:

#include <cstdint>
#include <iostream>
#include <typeinfo>

template <typename T> void func(const T& t) requires 
    std::is_same_v<T, uint8_t> || std::is_same_v<T, uint16_t>
{
    std::cout << "called with type " << typeid(T).name() <<std::endl;
}

int main()
{
     uint8_t x = 4;
     func(x);         //calls uint8_t version

     uint16_t y = 4;
     func(y);         //calls uint16_t version

     int z = 6;
//     func(z);  compiler error, z is not right type
}

Test in compiler explorer

Upvotes: 2

user12002570
user12002570

Reputation: 1

With C++20, we can use requires clause instead of SFINAE to do the same:

#include <cstdint>
#include <iostream>

template <typename T> void func(const T& t) requires std::is_same_v<T, uint8_t>
{
    std::cout << "uint8 version called" <<std::endl;
}
template <typename T> void func(const T& t) requires std::is_same_v<T, uint16_t>
{
    std::cout << "uint16 version called" <<std::endl;
}
int main()
{
     uint8_t x = 4;
     func(x);         //calls uint8 version

     uint16_t y = 4;
     func(y);         //calls uint16_t version
}

Upvotes: 1

user12002570
user12002570

Reputation: 1

This can be done by using function template and SFINAE as shown below:

#include <cstdint>
#include <iostream>

template <typename T> std::enable_if_t<std::is_same_v<T, uint8_t>> func(const T& t)
{
    std::cout << "uint8 version called" <<std::endl;
}
template <typename T> std::enable_if_t<std::is_same_v<T, uint16_t>> func(const T& t)
{
    std::cout << "uint16 version called" <<std::endl;
}
int main()
{
     uint8_t x = 4;
     func(x);         //calls uint8 version

     uint16_t y = 4;
     func(y);         //calls uint16_t version
}

Upvotes: 3

Related Questions