Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

specialize return type to void or const lvalue reference

I'm trying to accomplish the below..

enum class Options : uint8_t {    
    optA,
    optB,
    optC
};

class Test {
public:

    static std::string str;
    static std::vector<std::string> vec;

    template<Options option>
    static auto func()
    {
        if constexpr (option == Options::optA)
        {
            return str; // want this to deduce to 'const std::string', but only does so if 'const auto' (const std::string& also fine though)
        }
        else if constexpr (option == Options::optB)
        {
            return vec; // want this to deduce to 'const std::vector<std::string>&' but only does so if 'const auto&'
        }

        // want this to deduce to 'void' but only can if 'auto'
    }
}

But of course it doesn't work for the commented reasons.

I know I could...

1) specialize the function outside of the class body for each option, and deliberately specify the return type

or 2) explicitly pass in the return type when calling the function

But is there any cleaner solution where all I need to do is pass in the single Options value into the template and the rest gets derived inside of a single function body?

const std::string& value = Test::func<Options::optA>();
const std::vector<std::string>& values = Test::func<Options::optB>();
Test::func<Options::optC>();
like image 607
Gukki5 Avatar asked Jan 03 '20 20:01

Gukki5


2 Answers

For a non-static func(), you can mark it as const and use decltype(auto) deduction:

template<Options option>
decltype(auto) func() const
{
    if constexpr (option == Options::optA)
        return (str);
    else if constexpr (option == Options::optB)
        return (vec);
}

str and vec are parenthesized so that decltype(auto) deduce a reference type. For optA it will return const std::string&, for optBconst std::vector<std::string>&, and void otherwise.

Demo 1


For a static member function and static members you can write:

template<Options option>
static decltype(auto) func()
{
    if constexpr (option == Options::optA)
        return std::as_const(str);
    else if constexpr (option == Options::optB)
        return std::as_const(vec);
}

Demo 2

like image 121
Evg Avatar answered Nov 09 '22 11:11

Evg


There is no need to return a const std::string. You can just return a std::string and the caller can decide if it wants it to be const or not. If you are okay with that, then your function would become

template<Options option>
static decltype(auto) func()
{
    if constexpr (option == Options::optA)
    {
        return str;
    }
    else if constexpr (option == Options::optB)
    {
        return const_cast<const std::vector<std::string>&>(vec);
    }
    else        //
    {           // this bit is not really needed but I like 
        return; // being explicit that `void` is what we want to return
                //
    }           //
}

and now decltype(auto) will deduce for Options::optA a std::string, for Options::optB a const std::vector<std::string>& and for Options::optC , void.

like image 34
NathanOliver Avatar answered Nov 09 '22 12:11

NathanOliver