簡體   English   中英

在 std::conditional 中使用模板來確定 function 參數類型

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM