簡體   English   中英

如何在C ++中實現<<接口?

[英]How to implement a << interface in C++?

我正在使用一個小型編譯器,並且我使用boost::variant<bool, ClassA> info來存儲每個節點的信息。

當我調用std::cout<< node.info;時, boost::variant將自動調用特定類型的正確<<運算符std::cout<< node.info;

但是,由於ostream的內置格式化功能無法滿足我的要求(如果node.info==true並打印"string"代替string ,則打印#t而不是1 ),因此應引入新的bool / string類型。

我想實現一個模板類Wrapper<T> ,它的行為就像T (因為有很多舊代碼)並提供<<的接口。

首先,實現了以下版本:

template<typename T> class Wrapper : public T
{
    public:
        template<typename ... U> Wrapper(const U& ... a):T(a...) {}
        friend std::ostream& operator <<(std::ostream& o, const Wrapper<T>& w);
};

該版本對std::string效果很好,但是當T = bool時,由於不能繼承內置類型,因此將引發錯誤。

我當前的解決方法是使用部分專業化:

template<typename T, bool ISCLASS= std::is_class<T>::value> class Wrapper;

template<typename T> class Wrapper<T, false>
{
    private:
        T inner;
    public:
        template<typename ... U> Wrapper(const U& ... a): inner(a...) {}
        //Wrap the operators (= + += ...)
        template<typename U> Wrapper<T> operator !() { Wrapper<T> res(*this); res.inner=!res.inner; return res; }
        operator T() const{ return inner; }
        friend std::ostream& operator <<(std::ostream& o, const Wrapper<T>& w);
};

template<typename T> class Wrapper<T, true> : public T
{
    public:
        template<typename ... U> Wrapper(const U& ... a):T(a...) {}
        friend std::ostream& operator <<(std::ostream& o, const Wrapper<T>& w);
};

顯然,這不是一個完美的解決方案,因為我必須包裝bool所有運算符或任何其他內置類型。

任何幫助,將不勝感激。

謝謝。

我們可以考慮一些更簡單的方法嗎?

使用引用或指針創建一個簡單的包裝器。

template <class T>
struct MyFormattingWrapper
{
    const T& nucleus;
};

然后是工廠功能。

template <class T>
MyFormattingWrapper<T> my_formatting(const T& n)
{
    return MyFormattingWrapper<T>{ n };
}

然后,您可以專門設置每種類型的格式。

std::ostream& operator << (std::ostream& o, const MyFormattingWrapper<int>& w)
{
    return o << "int:" << w.nucleus;
}

std::ostream& operator << (std::ostream& o, const MyFormattingWrapper<std::string>& w)
{
    return o << "std::string:" << w.nucleus;
}


int main()
{
    std::cout << my_formatting(123) << std::endl;
    std::cout << my_formatting(std::string{ "abc" }) << std::endl;
}

更新:

C字符串可能是特例。 但這並不困難。

struct MyFormattingWrapper_c_string
{
    const char* const nucleus;
};

MyFormattingWrapper_c_string my_formatting(const char* n)
{
    return MyFormattingWrapper_c_string{ n };
}

MyFormattingWrapper_c_string my_formatting(char* n)
{
    return MyFormattingWrapper_c_string{ n };
}

std::ostream& operator << (std::ostream& o, const MyFormattingWrapper_c_string& w)
{
    return o << "c-string:" << w.nucleus;
}

Nicky C的答案很好,但是存在一個問題,即函數的部分專業化尚不能解決。 這意味着您不能生成適用於通用向量的版本,如下所示:

template<typename T>
std::ostream& operator << (std::ostream& o, const  MyFormattingWrapper<std::vector<T>>& vec)
{
    o << "vec:[ "
    for(auto v : vec) {
       o<<my_formatting(v);
       o<<" ";
    }
    return o<<"]"
}

您可以通過將專用輸出的核心放入MyFormattingWrapper類中並且僅使用一個operator<<來解決此問題。

// The default one
template <class T> struct MyFormattingWrapper {
    const T& nucleus;
    ostream& output(ostream & os) {
       return os<<nucleus;
    }
};

// Specialized for string
template <> struct MyFormattingWrapper<std::string> {
    const std::string& nucleus;
    ostream& output(ostream & os) {
       return os<<"string:"<<nucleus;
    }
};


// Specialized for vector
template <class T> struct MyFormattingWrapper<std::vector<T>> {
    const std::vector<T>& nucleus;
    ostream& output(ostream & os) {
       os<<"vec:[";
       for(auto & v: nucleus) {
         os<<my_formatting(v)<<" ";
       }
      return os<<"]";
    }
};

// Now there's just one of these, so partial template 
// specialization doesn't cause us any problems
template<typename T>
std::ostream& operator << (std::ostream& os, const MyFormattingWrapper<T>& w) {
    return w.output(os);
}

也許我最好跟進有關boost::variant另一個答案。

首先,從@MichaelAnderson學習,並考慮與boost::variant的互操作性,我想改進包裝器的設計。 我們添加了一個構造函數以啟用從核類型到包裝器類型的類型轉換。

template <class T>
class MyFormatting;

template <class T>
MyFormatting<T> my_formatting(const T& n)
{
    return MyFormatting <T>{n};
}

// int

template <>
class MyFormatting<int>
{
private:
    const int& nucleus;

public:
    MyFormatting(const int& n) : nucleus(n) {}

    friend std::ostream& operator << (std::ostream& os, const MyFormatting& w)
    {
        return os << "int:" << w.nucleus;
    }
};

// std::string

template <>
class MyFormatting<std::string>
{
private:
    const std::string& nucleus;

public:
    MyFormatting(const std::string& n) : nucleus(n) {}

    friend std::ostream& operator << (std::ostream& os, const MyFormatting& w)
    {
        return os << "std::string:" << w.nucleus;
    }
};

// c-string

template <>
class MyFormatting<char*>
{
private:
    const char* nucleus;

public:
    MyFormatting(const char* n) : nucleus(n) {}

    friend std::ostream& operator << (std::ostream& os, const MyFormatting& w)
    {
        return os << "c-string:" << w.nucleus;
    }
};


MyFormatting<char*> my_formatting(const char* n)
{
    return MyFormatting<char*>{n};
}

// std::vector

template <class T>
class MyFormatting<std::vector<T>>
{
private:
    const std::vector<T>& nucleus;

public:
    MyFormatting(const std::vector<T>& n) : nucleus(n) {}

    friend std::ostream& operator << (std::ostream& os, const MyFormatting& w)
    {
        os << "std::vector:[";
        for (const auto& x : w.nucleus)
        {
            os << x << " ";
        }
        os << "]";
        return os;
    }
};

接下來,讓我們使用帶有boost::variant的包裝器。 包裝器的構造函數允許在nuclues類型的變體之間轉換為包裝器的變體。

boost::variant<int, std::string> var_int = 50;
boost::variant<int, std::string> var_str = "fifty";
boost::variant<MyFormatting<int>, MyFormatting<std::string>> var_fmt_int = var_int;
boost::variant<MyFormatting<int>, MyFormatting<std::string>> var_fmt_str = var_str;

std::cout << var_int << " " << var_str << std::endl;
std::cout << var_fmt_int << " " << var_fmt_str << std::endl;

但是boost::variant<MyFormatting<int>, MyFormatting<std::string>>拼寫太長。 我們可以縮短它。

template <class... T>
using Variant_Formatting_t = boost::variant < MyFormatting<T>... > ;

std::cout << Variant_Formatting_t<int, std::string>{var_int} << " " << Variant_Formatting_t<int, std::string>{var_str} << std::endl;

由於boost::variant使用宏模板元編程來模擬可變參數模板,而不是使用C ++ 11可變參數模板,因此我無法使用類型推導使其變得更整潔。 這是我能到達的最遠的地方。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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