3

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.

CommanderBubble
  • 333
  • 2
  • 9
  • Do you want to convert numerical value of the enum to string? – yuri kilochek Oct 13 '20 at 01:00
  • It should be possible to use strongly typed enumerators in overload resolution. Unfortunately, `std::to_string` is not overloaded for any strongly typed enumerators. – Sam Varshavchik Oct 13 '20 at 01:11
  • Cast the enum to an `int` in the call to `to_string`? – Paul Sanders Oct 13 '20 at 01:18
  • "*it seems challenging - if not impossible - to get strongly typed enumerators to participate in overload resolution.*" No, it's only "challenging" to get strongly-typed enumerators to act like integers. Because that's the whole point of being "strongly-typed". – Nicol Bolas Oct 13 '20 at 01:25
  • no, in this case i don't want it to be an int, it's just the first of the errors that appear. i want the encode function to convert it to a string, as it should be selected from a list of strings. – CommanderBubble Oct 13 '20 at 02:00
  • the 'base' version of this is for handling actual integer, double, types, that can already run throw std::to_string. the enumerator should be done via a lookup in a list – CommanderBubble Oct 13 '20 at 02:01

1 Answers1

3

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.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Changing the derivation pattern does solve the problem. I guess it makes sense that the template arguments must also be valid. A bit annoying that it must derive separately, as there is shared functionality I now have to duplicate a bit of, but it does work. – CommanderBubble Oct 13 '20 at 02:35