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