1
votes

I've been trying to create specializations of templated class methods such that I would have different method definitions which the compiler selects from based on whether an argument is an integral type of an enum class, but clang gives me this error:

Out-of-line definition of 'ParseString' does not match any declaration in 'MyData'

While GCC gives the following:

error: prototype for 'typename std::enable_if::value, void>::type MyData::ParseString(T&, std::string)' does not match any in class 'MyData'

error: candidate is: template static void MyData::ParseString(T&, std::string)

The following code was used to produce these errors. I've experimented with other ways of using enable_if, such as in a template parameter rather than the return type, but nothing so far has worked. If I make the templated functions non-class functions instead of class methods, then they work fine. (Removing the static makes no difference.)

Is there a way to use enable_if for templated class methods, or a better way to achieve the same thing?

#include <string>
#include <type_traits>
#include <utility>

class MyData
{
public:
    MyData() = default;
    MyData(std::pair<std::string, std::string> string_representations);
    std::pair<std::string, std::string> ToStrings();

    enum class MyEnum
    {
        A = 0,
        B = 1
    };

    int integral_value;
    MyEnum enum_value;

private:

    template<typename T>
    static void ParseString(T&, std::string);
};

MyData::MyData(std::pair<std::string, std::string> string_representations)
{
    ParseString(integral_value, string_representations.first);
    ParseString(enum_value, string_representations.second);
}

std::pair<std::string, std::string> MyData::ToStrings()
{
    return std::make_pair(std::to_string(integral_value), std::to_string((unsigned long)enum_value));
}

template<typename T>
typename std::enable_if<std::is_enum<T>::value, void>::type
    MyData::ParseString(T& setting, std::string representation)
{
    setting = (T)std::stoul(representation);
}

template<typename T>
typename std::enable_if<std::is_integral<T>::value, void>::type
    MyData::ParseString(T& setting, std::string representation)
{
    setting = std::stoi(representation);
}

(In this example there's only one integral and one enum member of the class, but if you're wondering why the templated function would be useful, imagine if the class had multiple different enum and integral type members.)

1

1 Answers

1
votes

The error is telling you that the signature in the definition of your ParseString method doesn't match that found in the declaration inside the class. You have to copy the enable_if and use it in the declaration as well.

template<typename T>
static typename std::enable_if<std::is_integral<T>::value, void>::type ParseString(T&, std::string);

You also need another declaration of ParseString that uses enable_if<is_enum<T>> in its declaration, since you define another function that uses it:

template<typename T>
static typename std::enable_if<std::is_enum<T>::value, void>::type ParseString(T&, std::string);