[英]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
。
如果適當地支持該類型,您可以在編譯時進行測試:
std::string
)。begin
和end
功能,則使用這些。這可能如下所示:
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 << ']';
注意:
static
的,因為this
不在任何地方使用。operator<<
會更像 C++。std::begin
和std::end
代替。如果您需要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.