我如何专门针对std :: string

时间:2019-04-26 00:19:42

标签: c++ c++11 templates sfinae template-specialization

我具有以下模板化功能

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 个答案:

答案 0 :(得分:2)

我通常使用函数重载来解决此类问题。它使您可以轻松地将功能扩展为更多类型,并且仅在必要时才需要使用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 :(得分:1)

一种方法是将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;

答案 2 :(得分:1)

  

但是由于enable_if的默认类型所产生的歧义,它们似乎都失败了

问题是,如果你写

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

专业化与主模板不匹配,因为std::is_arithmetic<std::string>::value为假,因此第二个模板参数未启用。

一种可能的解决方案(我更喜欢bolov建议的解决方案,但只是为了探索其他方式并更好地理解问题)是使用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()版本都匹配,但是编译器选择第二个版本是因为它更专业。