2016-02-03 132 views
2

我目前正在編程我自己的PriorityQueue C++中的數據結構,我已將它編入模板類typename T根據模板類型在std :: to_string()和.toString()之間切換

我的類的toString()成員函數被定義爲:

/** 
* @brief Gives a std::string representation of this queue structure 
* 
* The priority queue is returned as a string in the following format: 
* 
* \code{.cpp} 
* Data Item  Priority 
* [Item]   [P1] 
* [Item]   [P2] 
* [Item]   [P3] 
* \endcode 
* 
* where P1 < P2 < P3. 
* 
* @return String representation of this priority queue 
*/ 
std::string toString() const { 

    std::string tempString = ""; 

    // initialise temporary node to front of priority queue 
    PQNode<T>* tempNode = front; 

    // append string with headers 
    tempString += "Data Item \t\t Priority\n"; 

    // while tempNode is not null, continue appending queue items to string 
    while (tempNode != nullptr) { 

     tempString += std::to_string(tempNode->head) + "\t\t\t " + std::to_string(tempNode->priority) + "\n"; 

     // shift tempNode to tail of current tempNode (moving along queue) 
     tempNode = tempNode->tail; 

    } 

    return tempString; 

} 

我怎樣才能編碼此使得std::to_string()是在此方法中使用,如果模板的類型是基本如intdouble.toString()(調用傳遞類的toString方法)是否被調用,如果模板的類型是一個具有自己的toString成員函數的類?

我認爲有一種方法可以用#ifndef子句做到這一點,但是我沒有太多使用這些的經驗。

回答

1

一種方法是使用函數重載。對所有具有toString函數的類型使用函數模板。對於需要使用的類型使用其他重載std::to_string

template <typename T 
std::string toString(T const& obj) 
{ 
    return obj.toString(); 
} 

std::string toString(int obj) 
{ 
    return std::to_string(obj); 
} 

std::string toString(double obj) 
{ 
    return std::to_string(obj); 
} 

,然後在PriorityQueue::toString使用

tempString += toString(tempNode->head) + ... ; 

6

首先,爲什麼這樣的時候有std::priority_queue

其次,通常有一個toString成員函數的設計錯誤,因爲它硬連線數據邏輯和數據表示。你應該做的是爲你的課程重載operator<<,這樣你就可以將實例傳遞給任何std::ostream。第三,你要求的是在編譯時選擇「正確的」字符串轉換函數,具體取決於類型是否具有toString成員函數,並使用std::to_string作爲後備。這可以在C++ 11中用一些模板和auto魔法很好地實現。這裏有一個例子:

#include <string> 
#include <iostream> 

struct Example 
{ 
    std::string toString() const { return "foo"; } 
}; 

template <class T> 
auto ToString(T const& t) -> decltype(t.toString()) 
{ 
    std::cout << "Using toString\n"; 
    return t.toString(); 
} 

template <class T> 
auto ToString(T const& t) -> decltype(std::to_string(t)) 
{ 
    std::cout << "Using std::to_string\n"; 
    return std::to_string(t); 
} 

int main() 
{ 
    Example e; 
    std::cout << ToString(e) << "\n"; 

    int i = 0; 
    std::cout << ToString(i) << "\n"; 
} 

輸出:

Using toString 
foo 
Using std::to_string 
0 

參見C++11 ways of finding if a type has member function or supports operator?(特別是由用戶 「ZAH」 的答案)。


不過,我強烈推薦實施operator<<的方法。

+0

嗯,我主要是爲了它的樂趣,練習用C++編寫數據結構(我喜歡保持知識新鮮)以及個人項目我希望比STL版本有更多的優先級隊列功能。感謝您提供的信息,我會仔細研究<<運算符的重載。 – ArchbishopOfBanterbury

+0

這比例很好。我們也很高興看到簡單的SFINAE帶有尾隨返回類型和'decltype'。另一種解決方案:實現非成員的'to_string'重載,然後使用'std :: to_string'做一個ADL +的東西,就像'swap'通常做的那樣。 – juanchopanza

+0

@ArchbishopOfBanterbury:「more functions」聽起來很糟糕。添加更多函數的C++方法就是添加更多的函數:)我的意思是獨立模板函數與迭代器一起工作,就像''中的所有函數一樣,目標是讓函數與*任何可以充當優先隊列的容器類。 –

1

使用已建立std::swap下一模式:

對於所有用戶類型在用戶類型本身的定義相同的命名空間定義一個非成員to_string功能。該功能可以調用成員toString

std::string to_string(const UserType& x) { 
    return x.toString(); 
} 

然後在你的函數調用to_string這樣的:

some_function() { 
    ... 
    using std::string; 
    ... 
    tempString += to_string(tempNode->head) ... // note: unqualified to_string! 
} 

該解決方案利用了C++的功能,稱爲ADL(參數依賴查找)。你可以在這裏閱讀更多關於它的信息:http://en.cppreference.com/w/cpp/language/adl