force a strongly typed enum to participate in template overload resolution

Viewed 56

As per this question - template argument deduction with strongly-typed enumerations - it seems challenging - if not impossible - to get strongly typed enumerators to participate in overload resolution.

if we have the following

#include <string>
#include <iostream>

void ExternalFunction(const std::string& tag, int value)
{
    std::cout << tag << " - (int) " << value << std::endl;
}

void ExternalFunction(const std::string& tag, double value)
{
    std::cout << tag << " - (double) " << value << std::endl;
}

void ExternalFunction(const std::string& tag, const std::string& value)
{
    std::cout << tag << " - (string) " << value << std::endl;
}

class Wrapper
{
    public:
        virtual void DoSomething() = 0;
};

template <typename variable_type>
class VariableWrapper : public Wrapper
{
    public:
        VariableWrapper(variable_type variable) : variable(variable) {}
        
        void DoSomething() override
        {
            ExternalFunction("tag", variable);
        }
        
        variable_type& variable;        
};

template <typename enumerator, typename convert_to_string>
class EnumWrapper : public VariableWrapper<enumerator>
{
    public:

        EnumWrapper(enumerator& variable, convert_to_string encoder) : VariableWrapper<enumerator>(variable), encoder(encoder) {}

        void DoSomething() override
        {
            ExternalFunction("tag", encoder(VariableWrapper<enumerator>::variable));
        }
        
        convert_to_string encoder;
};

enum class StronglyTyped
{
    A,
    B,
    C
};

int main()
{
    StronglyTyped e = StronglyTyped::A;
    Wrapper* wrapper = new EnumWrapper(e, [](StronglyTyped S)->std::string{return "Test";});
    wrapper->DoSomething();
}

if we try to run this - http://coliru.stacked-crooked.com/a/d555c4e3300ab05d - we get the errors

main.cpp: In instantiation of 'void VariableWrapper<variable_type>::DoSomething() [with variable_type = StronglyTyped]':
main.cpp:31:14:   required from here
main.cpp:33:29: error: no matching function for call to 'ExternalFunction(const char [4], StronglyTyped&)'
   33 |             ExternalFunction("tag", variable);
      |             ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~
main.cpp:4:6: note: candidate: 'void ExternalFunction(const string&, int)'
    4 | void ExternalFunction(const std::string& tag, int value)
      |      ^~~~~~~~~~~~~~~~
main.cpp:4:51: note:   no known conversion for argument 2 from 'StronglyTyped' to 'int'
    4 | void ExternalFunction(const std::string& tag, int value)
      |                                               ~~~~^~~~~
main.cpp:9:6: note: candidate: 'void ExternalFunction(const string&, double)'
    9 | void ExternalFunction(const std::string& tag, double value)
      |      ^~~~~~~~~~~~~~~~
main.cpp:9:54: note:   no known conversion for argument 2 from 'StronglyTyped' to 'double'
    9 | void ExternalFunction(const std::string& tag, double value)
      |                                               ~~~~~~~^~~~~
main.cpp:14:6: note: candidate: 'void ExternalFunction(const string&, const string&)'
   14 | void ExternalFunction(const std::string& tag, const std::string& value)
      |      ^~~~~~~~~~~~~~~~
main.cpp:14:66: note:   no known conversion for argument 2 from 'StronglyTyped' to 'const string&' {aka 'const std::__cxx11::basic_string<char>&'}
   14 | void ExternalFunction(const std::string& tag, const std::string& value)
      |                                               ~~~~~~~~~~~~~~~~~~~^~~~~

Is it possible to get the strongly typed enumerator to participate in the overload resolution? I don't want to remove the strongly typing - which does remove this issue - as i will then have to use unique names between multiple enums

EDIT: i have removed the std::to_string from the question and updated the code accordingly as it was incorrectly being focused on.

1 Answers

You have written your code incorrectly.

OK, so EnumWrapper<>::DoSomething overrides VariableWrapper<T>::DoSomething. So it won't be called by directly calling DoSomething on any EnumWrapper object.

But that doesn't mean VariableWrapper<T>::DoSomething doesn't exist. It does exist, and you can still call it by a qualified call. As such, when you instantiate VariableWrapper<T> for some particular T, the member functions of this template class must be valid.

And VariableWrapper<T>::DoSomething is not valid for any T which cannot be used to call ExternalFunction directly. This has nothing to do with enumerations specifically; it's purely about how you wrote your function.

You should instead make EnumWrapper not inherit from VariableWrapper. They both can inherit from BaseWrapper, but they have no business inheriting from one another. Alternatively you could make ExternalFunction itself a template that can resolve how to do whatever it is that it does for any given type.

Related