简体   繁体   English

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

[英]Prevent instantiation of template class for types not supported by stringstream extraction operator (>>)

I'm trying to learn a bit about templates and metafunctions, namely std::enable_if . 我正在尝试学习一些有关模板和元函数的信息,即std::enable_if I'm making a menu system for our school assignments (extracurricular, mind you), and need a way of getting input from the user. 我正在为我们的学校作业(课外活动,请注意)制作一个菜单系统,并且需要一种从用户那里获取输入的方法。 I'd like to define a template class for various types of input - something used along the lines of: 我想为各种类型的输入定义一个模板类-用于以下方面的东西:

std::string userInput = Input<std::string>("What's your name?").Show();
float userHeight = Input<float>("How tall are you?").Show();

I'd like to (and I'm sure there are reasons not to, but nevertheless) do this generalized sort of conversion using a std::stringstream : get input from user, feed into SS, extract into variable of type T. 我想(而且我确信有理由不这样做,但是尽管如此)使用std::stringstream进行这种广义的转换:从用户处获取输入,输入到SS中,提取到类型T的变量中。

It's easy enough to see if the conversion failed during runtime, but I'd like to use std::enable_if to prevent people from using my Input<> class for cases where conversion is impossible, say: 很容易看到转换在运行时是否失败,但是我想使用std::enable_if来防止人们在无法进行转换的情况下使用Input<>类,例如:

std::vector<Boats> = Input<std::vector<>>("Example").Show();

Obviously a std::stringstream cannot convert a string to a vector, so it will always fail. 显然, std::stringstream无法将字符串转换为向量,因此它将始终失败。

My question is this: 我的问题是这样的:

Can I format an std::enable_if clause to ONLY allow instantiation of my template class for the types listed above? 我可以格式化std::enable_if子句以仅允许实例化上述类型的模板类吗? Alternatively, is there a better way to go about it? 另外,还有更好的方法吗? Have I got things completely the wrong way around? 我是否有完全错误的方法?

What I've done so far 到目前为止我做了什么

I believe I have found a list of allowed types that std::stringstream can "convert" a string into: 我相信我已经找到了std::stringstream可以将字符串“转换”为的允许类型的列表:

http://www.cplusplus.com/reference/istream/istream/operator%3E%3E/ http://www.cplusplus.com/reference/istream/istream/operator%3E%3E/

I've been using std::enable_if like this up until this point: 到目前为止,我一直在使用std::enable_if

template <typename T, typename = typename 
std::enable_if<std::is_arithmetic<T>::value, T>::type>

However, now I'd like to extend it to allow not only arithmetic values, but all values supported by the sstream >> operator. 但是,现在我想扩展它以不仅允许算术值,而且允许sstream >>运算符支持的所有值。

If you prefer to use a SFINAE with a class template parameter, then you want 如果您希望将SFINAE与类模板参数一起使用,则需要

template <
    typename T,
    typename = decltype(std::declval<std::istringstream &>() >> std::declval<T &>(), void())
>
class Input /*...*/

I think that you are trying to use std::enable_if for something which doesn't require it. 我认为您正在尝试将std::enable_if用于不需要的内容。 If your template function already relies on operator<< applied on a generic type T , then compilation will fail in any case if the operator is not specialized for that type. 如果您的模板函数已经依赖于应用于泛型T operator<< ,则在任何情况下,如果该运算符不是专门针对该类型的,编译都将失败。

Nothing prevents you from using std::enable_if to solve your specific problem, though that may be not the best way to do it. 没有什么可以阻止您使用std::enable_if解决您的特定问题,尽管这可能不是解决问题的最佳方法。

If C++20 concepts were already largely adopted I'd say that that would be your way to go. 如果C ++ 20 概念已经被广泛采用,那我认为那是您的选择。

You could go the way as suggested here on SO and implement a class is_streamable which can check for that like this: 你可以去的方式在这里建议对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;
}

This would return one if the operator is implemented and zero if not. 如果实现了运算符,则返回1,否则返回零。

With that you can alter your snippet to 这样,您可以将代码段更改为

template <typename T, typename = typename 
std::enable_if<is_streamable<std::stringstream, C>::value, T>::type>

There are several thing you want: 您需要几件事:

  • a trait, is_streamable 特性,is_streamable
  • a way to forbid class instantiation. 一种禁止类实例化的方法。

For the traits, you might use std::experimental_is_detected or run your own: 对于特征,您可以使用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));

Then to forbid intantiation, several choices: 然后为禁止实例化,有几种选择:

static_assert : static_assert

template <typename T>
class Input
{
    static_assert(is_streamable<T>::value);
    // ...
};

or SFINAE friendly class: 或SFINAE友好课程:

template <typename T, typename = std::enable_if_t<is_streamable<T>>>
class Input
{
    // ...
};

so you allow to know if Input<T1> is valid. 这样就可以知道Input<T1>是否有效。

Notice that without all that stuff, your program won't compile anyway when instantiating the problematic method (hard error, so no SFINAE friendly). 请注意,没有所有这些东西,实例化有问题的方法(硬错误,因此对SFINAE不友好)时,您的程序将无法编译。

Being SFINAE friendly is not necessary most of the time. 在大多数情况下,不必对SFINAE友好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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