繁体   English   中英

防止实例化字符串流提取运算符(>>)不支持的类型的模板类

[英]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>

您需要几件事:

  • 特性,is_streamable
  • 一种禁止类实例化的方法。

对于特征,您可以使用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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM