我看到兩個問題與Simple的solution:
(1)未編譯這個測試用例
std::string hello = "hello";
const std::string earth = "earth";
Capitalize_And_Output(hello, "planet", earth);
因爲earth
是const std::string
並且沒有可以接受此呼叫的過載。 (試試吧!)
(2)未編譯可轉換爲std::string
,比如類型(比const char*
其他和類似的系統),
struct beautiful {
operator std::string() const {
return "beautiful";
}
};
Capitalize_And_Output(hello, beautiful{}, "planet", earth);
下實現解決了這些問題:
新的解決方案:我的舊解決方案(下)工作,但它不是效率爲char*
,char[N]
。另外,它很複雜,並且使用一些重載解析技巧來避免含糊不清。這個更簡單,更高效。
void Capitalize_And_Output_impl(const char* str) {
while (char c = toupper(*str++))
std::cout << c;
}
void Capitalize_And_Output_impl(std::string& str) {
std::transform(str.begin(), str.end(), str.begin(), toupper);
std::cout << str;
}
void Capitalize_And_Output_impl(const std::string& str) {
Capitalize_And_Output_impl(str.data());
}
template<typename First>
void Capitalize_And_Output(First&& str) {
Capitalize_And_Output_impl(std::forward<First>(str));
std::cout << '\n';
}
template<typename First, typename ... Strings>
void Capitalize_And_Output(First&& str, Strings&&... rest) {
Capitalize_And_Output_impl(std::forward<First>(str));
std::cout << ' ';
Capitalize_And_Output(std::forward<Strings>(rest)...);
}
因爲我不使用std::transform
(除第二過載),它並不需要知道字符串的大小提前。因此,對於char*
,不需要撥打std::strlen
(如在其他解決方案中那樣)。
需要注意的一個小細節是,此實現僅打印單詞之間的空格。 (它不會最後一個字後,打印一張。)
老辦法:
void Capitalize_And_Output_impl(std::string& str, int) {
std::transform(str.begin(), str.end(), str.begin(), ::toupper);
std::cout << str << ' ';
}
void Capitalize_And_Output_impl(std::string str, long) {
Capitalize_And_Output_impl(str, 0);
}
void Capitalize_And_Output() {
std::cout << '\n';
}
template<typename First, typename ... Strings>
void Capitalize_And_Output(First&& str, Strings&&... rest) {
Capitalize_And_Output_impl(std::forward<First>(str), 0);
Capitalize_And_Output(std::forward<Strings>(rest)...);
}
我想這兩個Capitalize_And_Output_impl
重載值得解釋。
首先忽視第二個參數(int
/long
)。第一個重載可以採用在出口處大寫的非const
左值(根據Trevor Hickney在Simple解決方案的評論中的要求)。
第二個過載意味着採取其他所有事情,即右值和const
左值。這個想法是將參數複製到一個左值,然後傳遞給第一個重載。這個功能自然可以用這種方式實現(仍然不考慮第二個參數):
void Capitalize_And_Output_impl(const std::string& str) {
std::string tmp(str);
Capitalize_And_Output_impl(tmp);
}
這個工作按需要。然而,Dave Abrahams着名的article解釋說,當你通過引用const
來引用參數並將其複製到你的函數中(如上所述)時,最好按值取值(因爲在某些情況下編譯器可能會避免複製) 。總之,本實施是優選的:
void Capitalize_And_Output_impl(std::string str) {
Capitalize_And_Output_impl(str);
}
不幸的是,作爲用於第一過載,則調用Capitalize_And_Output_impl
上左值也可以被引導到該過載。這產生了編譯器抱怨的含糊之處。這就是爲什麼我們需要第二個參數。
第一次過載需要int
,第二次需要long
。因此,通過字面0
,這是int
,使第一次過載優於第二次,但只有當出現歧義時。在其他情況下,即當第一個參數是左值或右值時,第一個過載不能被使用,而第二個過程可以在文字0
被提升爲long
之後。
最後兩句話。 (1)如果你想避免在Capitalize_And_Output
(我想這只是一個味道問題)的遞歸調用,那麼你可以使用與Simple的解決方案相同的詭計(通過unpack
)和(2)我沒有看到需要像在Simple的解決方案中一樣傳遞包裝::toupper
。
使用'std :: begin'和'std :: end'。實際上,字符串文字不是'char *',甚至不是'const char *'。它們是'const char [N]'。 – chris
一個快速而骯髒的解決方案是將第一個參數設置爲「std :: string」而不是類型推導的參數,並依賴於現有的隱式轉換。 –
@chris:但是這會給出數組的末尾(包括終止符),而不是字符串的末尾(不包括終止符)。 –