[英]Using templates in std::conditional to determine function argument types
我希望通過相同的功能將所有數據保存和加載到 go 以減少出現錯誤的機會。 為此,我使用了很多模板(以及很多 function 重載)。 它起作用了,我的代碼現在更干凈了,但是我無法使用const
進行保存(因為它通過與加載程序相同的功能,其中數據保持非常量)。
我想正確使用const
,所以這里嘗試讓一個簡單的版本工作,其中數據(在本例中為std::vector
)對於std::ifstream
是非常量的,否則為const
:
#include <iostream>
#include <fstream>
#include <vector>
template <class Foo>
void Overload(const Foo & foo)
{
std::cout << "went to const" << std::endl;
}
template <class Foo>
void Overload(Foo & foo)
{
std::cout << "went to non-const" << std::endl;
}
template <class StreamType, typename... Arguments>
void ReadOrWrite (
/* for 1st argument */ StreamType & filestream,
/* type for 2nd argument */ typename std::conditional<
/* if */ std::is_same<StreamType, std::ifstream>::value,
/* then */ std::vector<Arguments...>,
/* else */ const std::vector <Arguments...>
>::type
/*2nd argument name */ & vector
)
{
Overload(vector);
}
int main ()
{
std::ofstream output_filestream;
std::ifstream intput_filestream;
std::vector<int> vector;
ReadOrWrite(output_filestream, vector);
ReadOrWrite(intput_filestream, vector);
return 0;
}
我知道如果我編輯 function 調用,它將正確編譯/運行:
ReadOrWrite<std::ofstream, int>(output_filestream, vector);
ReadOrWrite<std::ifstream, int>(intput_filestream, vector);
但我不希望 function 的用戶需要在 function 調用期間列出類型。
有沒有一種干凈的方法來做我建議的事情?
編輯
我的動機的合法性似乎存在疑問。
我沒有徹底解釋我的動機,因為它不是太簡單(也不是太復雜),我尊重讀者的時間。
我給出的示例是我無法解決的最簡單的部分——“重載”函數只是被包括在內以查看它是否有效。
但是,似乎我缺乏解釋引起了混亂,所以我將闡述:
我制作了一個小型庫來處理一般情況下的數據保存和加載。 它通過使用以下接口成功地允許用戶的類具有簡單的保存/加載方法:
class SomeClass
{
public:
template <class StreamType>
void SaveOrLoad(StreamType & filestream)
{
saveload::SaveToOrLoadFromFile(filestream,
data_1_,
data_2_,
/* ..., */
data_n_,
);
}
void SaveToFile (const std::string & filename)
{
std::ofstream output_filestream(filename, std::ios::binary);
// file handling
SaveOrLoad(output_filestream);
}
void LoadFromFile (const std::string & filename)
{
std::ifstream input_filestream(ptf::problem_input_file, std::ios::binary);
// file handling
SaveOrLoad(input_filestream);
}
};
該庫處理所有基本數據類型、STL 容器以及使用正確SaveOrLoad(StreamType &)
接口的任何其他容器,包括所有容器的保存和調整大小。 該庫已通過相同的確定性函數強制所有保存和加載到 go,因此完全消除了涉及保存/加載不匹配的錯誤的可能性(除非用戶濫用庫的簡單界面)。
我的庫遇到的問題——以及我提出問題的原因——是一個理論上的問題,因為我目前不需要它: SaveToFile
方法應該能夠是const
。
最好的建議是提供兩個獨立的函數,因為讀取和寫入是兩個不同的操作,無論您發送什么類型。 例如,有人可能是輸入和 output 的fstream
。 僅僅通過類型系統,您無法知道意圖。 閱讀或寫作的決定通常是一個意圖,這些很少嵌入到類型系統中。
由於保存和加載是不同的操作,因此應該是不同的功能(可能在它們之間共享代碼)
如果你真的想要一個 function 兩者兼而有之並在類型之間切換,那么我建議限制輸入或 output 的功能:
// output streams
template <class StreamType, typename... Arguments,
typename std::enable_if<std::is_base_of<std::ostream, StreamType>::value, int>::type = 0
>
void ReadOrWrite (
StreamType & filestream,
std::vector<Arguments...> const& vector
) {
Overload(vector);
}
// input streams
template <class StreamType, typename... Arguments,
typename std::enable_if<std::is_base_of<std::istream, StreamType>::value, int>::type = 0
>
void ReadOrWrite (
StreamType& inputstream,
std::vector<Arguments...>& vector
) {
Overload(vector);
}
由於第二個比第一個更專業,因此只要 stream 是std::istream
並且向量是可變的,就會采用它。 否則,將采用第一個。
另一個重載解決方案可以轉換ReadOrWrite()
,幾乎就像您在問題中所寫的那樣,在幫助器 function 中
template <typename ... Args, typename ST>
void ReadOrWrite_helper (ST &, typename std::conditional<
std::is_same<ST, std::ifstream>::value,
std::vector<Args...>,
std::vector<Args...> const>::type vec)
{ Overload(vec); }
adding a overloaded couple of ReadOrWrite()
function to select select the Args...
and explicit them calling the helper function
template <typename ... Ts>
void ReadOrWrite (std::ifstream & is, std::vector<Ts...> & vec)
{ ReadOrWrite_helper<Ts...>(is, vec); }
template <typename ... Ts>
void ReadOrWrite (std::ofstream & is, std::vector<Ts...> const & vec)
{ ReadOrWrite_helper<Ts...>(is, vec); }
請注意,鑒於Args...
類型處於非推導上下文中,因此需要說明,我已將它們放在ReadOnWrite_helper()
模板參數聲明中,位於ST
之前; 所以也不需要明確ST
。
另請注意,如果您不需要知道ReadOrWrite_helper()
中的Args...
類型,一切都會變得更簡單
template <typename V, typename ST>
void ReadOrWrite_helper (ST &, V & vec)
{ Overload(vec); }
template <typename V>
void ReadOrWrite (std::ifstream & is, V & vec)
{ ReadOrWrite_helper(is, vec); }
template <typename V>
void ReadOrWrite (std::ofstream & is, V const & vec)
{ ReadOrWrite_helper(is, vec); }
也消除了解釋V
型的需要。
首先,道歉,我對我的問題的解釋很糟糕,我將來會做得更好。
以防萬一有人碰巧讀到這個並且有類似的問題,我通過引入兩個包裝函數來解決它,其目的是明確 state 模板參數:
template <class DataType>
void ParseData (std::ofstream & output_filestream, const DataType & data)
{
ReadOrWrite<std::ofstream, DataType> (output_filestream, data);
}
template <class DataType>
void ParseData (std::ifstream & input_filestream, DataType & data)
{
ReadOrWrite<std::ifstream, DataType> (input_filestream, data);
}
重要的部分是這個解決方案是可擴展的:為了處理每種新的數據類型,我只需要編寫一個ReadOrWrite
function,在 function 調用中沒有不必要的模板參數。
ParseData
函數如何適應解決方案:
#include <iostream>
#include <fstream>
#include <vector>
// lots of useful typetraits:
#include <type_traits>
template <class S, class T = void>
struct is_vector : std::false_type {};
template <class S, class T>
struct is_vector <std::vector<S,T>> : std::true_type {};
template <typename Datatype>
inline constexpr bool is_vector_v = is_vector<Datatype>::value;
// Kinda similar format to my serialization library:
template <class Foo>
void Overload(const Foo & foo)
{
std::cout << "went to const" << std::endl;
}
template <class Foo>
void Overload(Foo & foo)
{
std::cout << "went to non-const" << std::endl;
}
// Special type trait specific to this library
// (within an anonymous namespace)
template <typename, typename Data>
struct vet_data_constness
: std::add_const<Data> {};
template <typename Data>
struct vet_data_constness <std::ifstream, Data>
: std::remove_const<Data> {};
template <typename StreamType, typename Data>
using vet_data_constness_t
= typename vet_data_constness<StreamType,Data>::type;
template <class StreamType, typename DataType>
std::enable_if_t<is_vector_v<DataType>>
ReadOrWrite (StreamType & filestream,
vet_data_constness_t<StreamType, DataType> & vector)
{
Overload(vector);
}
// These functions are simply for routing back to the ReadOrWrite
// funtions with explicit calls
template <class DataType>
void ParseData (std::ofstream & output_filestream, const DataType & data)
{
ReadOrWrite<std::ofstream, DataType> (output_filestream, data);
}
template <class DataType>
void ParseData (std::ifstream & input_filestream, DataType & data)
{
ReadOrWrite<std::ifstream, DataType> (input_filestream, data);
}
int main ()
{
std::ofstream output_filestream;
std::ifstream intput_filestream;
std::vector<int> vector;
ParseData(output_filestream, vector);
ParseData(intput_filestream, vector);
return 0;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.