我如何专门针对 std::string 我的模板

How do i specialize my template for std::string

我有以下模板函数

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> 替换为调度标签的非常丑陋的解决方案。

谢谢!

一种方法是将 SFINAE 移动到 return 类型:

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;

but they all seem to fail due to the ambiguity generated by the default type of the enable_if

问题是如果你写

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

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

一个可能的解决方案(我更喜欢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() 版本,但编译器选择第二个,因为它更专业。

我通常使用函数重载来解决此类问题。它允许您轻松扩展更多类型的功能,并且您只需要在必要时使用 SFINAE(例如,对于 std::is_arithmetic)。由于您不能按 return 类型重载,因此仅当您将结果存储在其中一个参数中时才有效。

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;
}