Невозможно заставить `std :: enable_if` работать для перечисления с незаданной областью - PullRequest
0 голосов
/ 16 июня 2020

Я пытаюсь создать служебную функцию ToString, которая либо вызывает std::to_string, либо пользовательский метод to_string, определенный где-то еще. Вот что я придумал для версии 1.0:

Контекст

Код здесь очень близок к тому, с чем я работаю для контекста. У меня есть перечисления из сторонней библиотеки, которые определены с использованием определения C stlye, а также перечисления, которые я определил с использованием стиля C ++.

//From my 3rd party library
namespace ThirdPartyNamespace
{
    typedef enum
    {
        UnscopedValue
    } Unscoped;
}

//Defined in Scoped.h
namespace MyNamespace 
{
    enum class Scoped
    {
        ScopedValue
    };

    static std::string to_string(Scoped value)
    {
        return "Enums::Scoped";
    }
}

//Defined in Helpers.h, contains to_string methods for the enums in the 3rd party library
namespace Helpers 
{
    static std::string to_string(ThirdPartyNamespace::Unscoped value)
    {
        return "Enums::Unscoped";
    }    
}

Код вызова

ThirdPartyNamespace::Unscoped x = ThirdPartyNamespace::UnscopedValue;
MyNamespace::Scoped y = MyNamespace::Scoped::ScopedValue;

std::cout << Utilities::ToString(x) << std::endl;
std::cout << Utilities::ToString(y) << std::endl;

Код утилиты

namespace Utilities
{
    template <typename T>
    std::string ToString(const T& value)
    {
        using std::to_string;

        return to_string(value);
    }
}

Это компилируется и работает для Scoped, но записывает целочисленное значение для Unscoped. Я провел небольшое исследование, и, похоже, чтобы это исправить, мне нужно было использовать std::enable_if для моих типов перечислений. После некоторого исследования того, как работает std::enable_if и SFINAE, я придумал то, что, как я думал, будет работать:

Utility Code

template<typename T, std::enable_if_t<!std::is_enum<T>::value>* = nullptr>
static std::string ToString(const T& value)
{
    using std::to_string;

    return to_string(value);
}

template<typename T, typename std::enable_if<std::is_enum<T>::value>::type* = nullptr>
static std::string ToString(const T& value)
{
    return to_string(value);
}

Однако это не компилируется . В частности, для Unscoped он выдает ошибку 'to_string': identifier not found (я подтвердил, что это была ошибка, закомментировав вызов с помощью Unscoped, и он скомпилировался и работал, как ожидалось).

У меня вопрос: почему компилятор не может найти мой собственный метод to_string?

Дополнительный вопрос: из моего чтения я обнаружил, что ::type* = nullptr "устанавливает значение по умолчанию для параметра 'type' шаблона, равное 'nullptr'" ( Source ), что именно означает ли это, и зачем здесь значение по умолчанию?

1 Ответ

1 голос
/ 16 июня 2020

Вам нужно либо

  • переместить

    std::string to_string(ThirdPartyNamespace::Unscoped value)
    

    в пространство имен ThirdPartyNamespace, чтобы его можно было найти благодаря ADL

  • или измените ToString на

    template <typename T>
    std::string ToString(const T& value)
    {
        using std::to_string;
        using Helpers::to_string;
    
        return to_string(value);
    }
    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...