簡體   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