我具有以下模板化功能
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>
的非常丑陋的解决方案。
谢谢!
答案 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()
版本都匹配,但是编译器选择第二个版本是因为它更专业。