[英]Prevent instantiation of template class for types not supported by stringstream extraction operator (>>)
我正在尝试学习一些有关模板和元函数的信息,即std::enable_if
。 我正在为我们的学校作业(课外活动,请注意)制作一个菜单系统,并且需要一种从用户那里获取输入的方法。 我想为各种类型的输入定义一个模板类-用于以下方面的东西:
std::string userInput = Input<std::string>("What's your name?").Show();
float userHeight = Input<float>("How tall are you?").Show();
我想(而且我确信有理由不这样做,但是尽管如此)使用std::stringstream
进行这种广义的转换:从用户处获取输入,输入到SS中,提取到类型T的变量中。
很容易看到转换在运行时是否失败,但是我想使用std::enable_if
来防止人们在无法进行转换的情况下使用Input<>
类,例如:
std::vector<Boats> = Input<std::vector<>>("Example").Show();
显然, std::stringstream
无法将字符串转换为向量,因此它将始终失败。
我可以格式化std::enable_if
子句以仅允许实例化上述类型的模板类吗? 另外,还有更好的方法吗? 我是否有完全错误的方法?
我相信我已经找到了std::stringstream
可以将字符串“转换”为的允许类型的列表:
http://www.cplusplus.com/reference/istream/istream/operator%3E%3E/
到目前为止,我一直在使用std::enable_if
:
template <typename T, typename = typename
std::enable_if<std::is_arithmetic<T>::value, T>::type>
但是,现在我想扩展它以不仅允许算术值,而且允许sstream >>
运算符支持的所有值。
如果您希望将SFINAE与类模板参数一起使用,则需要
template <
typename T,
typename = decltype(std::declval<std::istringstream &>() >> std::declval<T &>(), void())
>
class Input /*...*/
我认为您正在尝试将std::enable_if
用于不需要的内容。 如果您的模板函数已经依赖于应用于泛型T
operator<<
,则在任何情况下,如果该运算符不是专门针对该类型的,编译都将失败。
没有什么可以阻止您使用std::enable_if
解决您的特定问题,尽管这可能不是解决问题的最佳方法。
如果C ++ 20 概念已经被广泛采用,那我认为那是您的选择。
你可以去的方式在这里建议对SO和实现类is_streamable
它可以检查,像这样:
#include <type_traits>
#include <utility>
#include <iostream>
#include <sstream>
template<typename S, typename T>
class is_streamable
{
template<typename SS, typename TT>
static auto test(int)
-> decltype(std::declval<SS&>() << std::declval<TT>(), std::true_type());
template<typename, typename>
static auto test(...)->std::false_type;
public:
static const bool value = decltype(test<S, T>(0))::value;
};
class C
{
public:
friend std::stringstream& operator<<(std::stringstream &out, const C& c);
};
std::stringstream& operator<<(std::stringstream& out, const C& c)
{
return out;
}
int main() {
std::cout << is_streamable<std::stringstream, C>::value << std::endl;
return 0;
}
如果实现了运算符,则返回1,否则返回零。
这样,您可以将代码段更改为
template <typename T, typename = typename
std::enable_if<is_streamable<std::stringstream, C>::value, T>::type>
您需要几件事:
对于特征,您可以使用std::experimental_is_detected
或自行运行:
template <typename T>
auto is_streamable_impl(int)
-> decltype (T{},
void(), // Handle evil operator ,
std::declval<std::istringstream &>() >> std::declval<T&>(),
void(), // Handle evil operator ,
std::true_type{});
template <typename T>
std::false_type is_streamable_impl(...); // fallback, ... has less priority than int
template <typename T>
using is_streamable = decltype(is_streamable_impl<T>(0));
然后为禁止实例化,有几种选择:
static_assert
:
template <typename T>
class Input
{
static_assert(is_streamable<T>::value);
// ...
};
或SFINAE友好课程:
template <typename T, typename = std::enable_if_t<is_streamable<T>>>
class Input
{
// ...
};
这样就可以知道Input<T1>
是否有效。
请注意,没有所有这些东西,实例化有问题的方法(硬错误,因此对SFINAE不友好)时,您的程序将无法编译。
在大多数情况下,不必对SFINAE友好。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.