Как мне специализировать мой шаблон для std :: string - PullRequest
0 голосов
/ 26 апреля 2019

У меня есть следующая шаблонная функция

template <typename As, typename std::enable_if<
std::is_arithmetic<As>::value, As>::type* = nullptr   > 
As getStringAs(const std::string& arg_name)
{
    std::istringstream istr(arg_name);
    As val;
    istr >> val;
    if (istr.fail())
        throw std::invalid_argument(arg_name);
    return val;
}

И я хотел бы использовать ее так:

getStringAs<float>("2.f");

Что было бы хорошим способом специализировать функцию для std::string чтобы я мог написать

getStringAs<std::string>("2.f");

Я перепробовал все свои известные способы, но все они, похоже, потерпели неудачу из-за неоднозначности, генерируемой типом std::enable_if по умолчанию.Например: если я напишу:

template<>
std::string getStringAs<std::string>(const std::string& arg_name)
{    
}

Это не будет соответствовать какой-либо перегрузке шаблона.Если я добавлю второй тип, это сгенерирует ошибку неоднозначности.Я попробовал Google-In, но единственное, что я мог найти, было об отправке тегов, но это сделало бы вызов уродливым на стороне пользователя.Я думал об очень уродливом решении использовать макроопределение для замены getStringAs<std::string> тегом отправки.

Спасибо!

Ответы [ 3 ]

2 голосов
/ 26 апреля 2019

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

template <typename T>
typename std::enable_if<std::is_arithmetic<T>::value>::type
getStringAsImpl(std::string const& in, T& out)
{
    std::istringstream sstr(in);
    sstr >> out;
    if (sstr.fail())
    {
        throw std::invalid_argument(in);
    }
}

void getStringAsImpl(std::string const& in, std::string& out)
{
    out = in;
}

template <typename T>
T getStringAs(std::string const& in)
{
    T out;
    getStringAsImpl(in, out);
    return out;
}
1 голос
/ 26 апреля 2019

но все они, похоже, дают сбой из-за неоднозначности, генерируемой типом по умолчанию enable_if

Проблема в том, что если вы напишите

template <> 
std::string getStringAs<std::string> (const std::string& arg_name)
 { return arg_name; }

специализация не соответствует основному шаблону, поскольку std::is_arithmetic<std::string>::value имеет значение false, поэтому второй параметр шаблона не включен.

Возможное решение (я предпочитаю решение, предложенное Боловым, но просто для изучения других способов и лучшего понимания проблемы) - включить второй параметр шаблона с помощью std::string следующим образом

template <typename As, typename std::enable_if<
      std::is_arithmetic<As>::value 
   || std::is_same<As, std::string>::value, bool>::type = true> 
As getStringAs(const std::string& arg_name)
{
    std::istringstream istr(arg_name);
    As val;
    istr >> val;
    if (istr.fail())
        throw std::invalid_argument(arg_name);

    return val;
}

теперь вы можете полностью специализироваться как обычно

template <> 
std::string getStringAs<std::string> (const std::string& arg_name)
 { return arg_name; }

Обратите внимание, что std::string теперь соответствует обеим версиям getStringAs(), но компилятор выбирает вторую, потому что он более специализированный.

1 голос
/ 26 апреля 2019

Один из способов - переместить SFINAE в тип возврата:

template <typename As>
auto getStringAs(const std::string& arg_name)
    -> typename std::enable_if<std::is_arithmetic<As>::value, As>::type;

template <typename As>
auto getStringAs(const std::string& arg_name)
    -> typename std::enable_if<std::is_same<As, std::string>::value, As>::type;
...