我如何专门针对 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;
}
我有以下模板函数
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;
}