1

I have a class, which wraps an enum and provides string conversion for it. Now I introduced the template parameter 'fastStringConvert' which controls how the conversion made using SFINAE (found here: how can I use std::enable_if in a conversion operator?). The code compiles under MSVC, but fails under GCC and Clang.

error: no type named ‘type’ in ‘struct std::enable_if<false, void>’

What could be the problem and how should I change the code?

The relevant parts of the code below or here: http://rextester.com/SYC74124

#include <map>
#include <string>
#include <type_traits>

template <
    class SubClass, 
    typename EnumType, 
    bool fastStringConvert = true
>
class SmartEnum
{
public:
    template <
        typename SFINAEPostponer = EnumType,
        typename = typename std::enable_if<fastStringConvert, void>::type
    >
    explicit operator const std::string&() const 
    {
        auto name = SubClass::names().find((int)value);
        if (name != SubClass::names().end())
        {
            return name->second;
        }
        else
        {
            static const std::string na("n.a.");
            return na;
        }
    }

    template <
        typename SFINAEPostponer = EnumType,
        typename = typename std::enable_if<!fastStringConvert, void>::type
    >
    explicit operator const std::string() const
    {
        auto name = SubClass::names().find((int)value);
        if (name != SubClass::names().end()) return name->second;
        else return std::to_string((int)value);
    }

protected:
    typedef const std::map<int, std::string> Names;
    EnumType value;
};


enum class Foo_type : int { a, b, c }; 

struct Foo : SmartEnum<Foo, Foo_type, true>
{ 
    typedef SmartEnum<Foo, Foo_type, true> Base; 

    static const Base::Names &names() 
    { 
        static const Base::Names names = { { 0, "a" }, { 1, "b" }, { 2,"c" }}; 
        return names; 
    } 
};
simon
  • 1,210
  • 12
  • 26

1 Answers1

5

You have to use template argument from the method, else you have a hard error, something like:

template <
    typename SFINAEPostponer = EnumType,
    bool Cond = !fastStringConvert,
    typename = typename std::enable_if<Cond, void>::type
>
explicit operator const std::string() const

BTW, better to use enable_if as type instead of default value (to allow disable part):

template <
    typename SFINAEPostponer = EnumType,
    bool Cond = !fastStringConvert,
    typename std::enable_if<Cond, void>::type* = nullptr
>
explicit operator const std::string() const
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Thanks, it helped. I don't even need the first argument this way. – simon Jul 18 '17 at 08:28
  • Could you explain what is the difference between this two solution? – simon Jul 18 '17 at 08:33
  • @simon you can't overload a function only base on default value of template parameter, just like you can't overload function on base of default value of its parameter. – W.F. Jul 18 '17 at 08:35
  • 1
    I recently watched a video by Stephan T. Lavavej where he recommended to use `typename std::enable_if::type = nullptr` because there is nothing that can implicitly convert to `void**`. – Henri Menke Jul 18 '17 at 08:54