Запретить создание экземпляра класса шаблона для типов, не поддерживаемых оператором извлечения stringstream (>>) - PullRequest
0 голосов
/ 31 января 2019

Я пытаюсь немного узнать о шаблонах и метафункциях, а именно 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 >>.

Ответы [ 4 ]

0 голосов
/ 31 января 2019

Есть несколько вещей, которые вы хотите:

  • черта, 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 в большинстве случаев не обязательно.

0 голосов
/ 31 января 2019

Я думаю, что вы пытаетесь использовать std::enable_if для чего-то, что не требует этого.Если ваша шаблонная функция уже использует operator<<, примененный к универсальному типу T, то компиляция завершится неудачей в любом случае, если оператор не специализирован для этого типа.

Ничто не мешает вам использовать std::enable_ifчтобы решить вашу конкретную проблему, хотя это может быть не лучшим способом сделать это.

Если бы C ++ 20 концепции уже были в значительной степени приняты, я бы сказал, что это будет ваш путьидти.

0 голосов
/ 31 января 2019

Вы можете пойти по пути, указанному здесь для 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;
}

Это вернет единицу, если операторреализован и ноль, если нет.

С этим вы можете изменить свой фрагмент на

template <typename T, typename = typename 
std::enable_if<is_streamable<std::stringstream, C>::value, T>::type>
0 голосов
/ 31 января 2019

Если вы предпочитаете использовать SFINAE с параметром шаблона класса, тогда вы хотите

template <
    typename T,
    typename = decltype(std::declval<std::istringstream &>() >> std::declval<T &>(), void())
>
class Input /*...*/
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...