簡體   English   中英

如何在 C++17 中將模板打印到控制台?

[英]How to print a template to console in C++17?

我有這個基本模板類:

template<typename Type>
struct InputBase {
    virtual void DisplaySingleResult(Type const& res) const {
        std::cout << res << std::endl;
    }
};

因此,下面的代碼無法編譯,因為vector沒有<<運算符

struct Input : InputBase<std::vector<std::vector<int>>> {
    void DisplaySingleResult(std::vector<std::vector<int>> const& results) const override {
        for (std::vector<int> const& result : results) {
            std::cout << "[" << result[0] << "," << result[1] << "]";
        }
        std::cout << std::endl;
    }
};

編譯時,方法InputBase::DisplaySingleResult編譯失敗,因為它使用vector類型。

一種解決方法是使虛擬InputBase::DisplaySingleResult方法純虛擬。

但是,我希望在我的基類中有一個具有相同簽名的DisplaySingleResult方法,以避免重寫所有其他派生類。 如何在 C++17 中做到這一點?

解決方案暫定:InputBase::DisplaySingleResult中,使用前檢查模板 Type 是否有<<運算符。

我不建議直接為std::ostream& operator<<(std::ostream&,const std::vector<T>&)提供重載(不確定是否允許)。 而是間接的。

而不是調用std::cout << res << std::endl; 你可以調用一個函數:

template<typename Type>
void print_result(const Type& res) { std::cout << res << std::endl; }

template<typename Type>
struct InputBase {
    virtual void DisplaySingleResult(Type const& res) const {
        print_result(res);
    }
};

現在您可以為沒有operator<<的任何類型提供重載,例如

void print_result(const std::vector<int>& v) { for (const auto& e : v) print_result(e); } 

為了更靈活地使用專業化,您可能需要考慮將print_result類模板而不是函數模板。 例如,當T具有begin()end()時,可能會有一個專門化元素調用print_result

如果適當地支持該類型,您可以在編譯時進行測試:

  1. 如果已經有合適的輸出運算符可用,請使用它(如果可以直接輸出類型,則此測試可確保您不使用迭代器,例如std::string )。
  2. 否則,如果類型具有beginend功能,則使用這些。
  3. 否則我們將處於錯誤狀態!

這可能如下所示:

template<typename Type>
struct InputBase
{
    static void displaySingleResult(Type const& res)
    {
        if constexpr (decltype(has_operator_out(res))::value)
        {
            std::cout << res;
        }
        else if constexpr
        (
            decltype(has_begin(res))::value && decltype(has_end(res))::value
        )
        {
            // your own desired formatting, e.g.:
            std::cout << '[';
            for(auto& r : res)
            {
                std::cout << r << ' ';
            }
            std::cout << ']';
        }
        else
        {
            // error handling:
            std::cout << "<invalid!>";
        }
    }

private:
    template <typename T>
    static auto has_operator_out(T& t)
        -> decltype(std::cout << t, std::true_type());
    static std::false_type has_operator_out(...);
    template <typename T>
    static auto has_begin(T& t)
        -> decltype(t.begin(), std::true_type());
    static std::false_type has_begin(...);
    template <typename T>
    static auto has_end(T& t)
        -> decltype(t.end(), std::true_type());
    static std::false_type has_end(...);
};

相反,遞歸調用該函數也允許打印嵌套容器(感謝463035818_is_not_a_number的提示):

std::cout << '[';
for(auto& r : res)
{
    InputBase<std::remove_cv_t<std::remove_reference_t<decltype(r)>>>
        ::displaySingleResult(r);
    std::cout << ' ';
}
std::cout << ']';

注意:

  1. ...該功能可以是static的,因為this不在任何地方使用。
  2. ...定義一個operator<<會更像 C++。
  3. ...您可以提供進一步的測試或根據您的需要調整現有的測試,例如使用std::beginstd::end代替。
  4. ...這些測試還有其他可能的模式,您可能想玩一下;)

如果您需要DisplaySingleResult()是虛擬的,典型的方法是委托給一個非虛擬函數,它可以是一個模板:

virtual void DisplaySingleResult(Type const& res)
{
    DisplaySingleResultImpl(res);
}

然后你可以模板化DisplaySingleResultImpl() 現在,在許多情況下,您不想在基類中執行此操作(或只想提供典型的實現)——在這些情況下,會想到 CRTP:

template<typename Type, typename Derived>
struct InputBase {
    virtual void DisplaySingleResult(Type const& res) const {
        static cast<const Derived&>(*this).DisplaySingleResultImpl(res);
    }
};

template<typename Type, typename Derived>
struct InputBaseWithCoutPrint : InputBase<Type, Derived> {
    void DisplaySingleResultImpl(Type const& res) const {
        std::cout << res << std::endl;
    }
};

CRTP 是一個非常強大的工具,不要過度使用它。

暫無
暫無

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

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