[英]Template Specialization: Const& versus Const*
以下代碼可以正常工作並打印:
你好
世界末日
class TemplateTester
{
public:
template<class T>
void Print(T& obj) { obj.PrintNewline(); }
template<class T>
void Print(T* obj) { obj->Print(); /*obj->Test();*/ }
};
class Printer
{
public:
void PrintNewline() const { std::cout << m_string.c_str() << "\r\n"; }
void Print() const { std::cout << m_string.c_str(); }
void Test() { m_string = "Oops"; }
std::string m_string;
};
int main()
{
Printer print1, print2;
print1.m_string = "Hello";
print2.m_string = "World";
TemplateTester tester;
tester.Print(print1);
tester.Print(&print2);
std::cout << " End ";
return 0;
}
但是,如果將打印功能更改為
void Print(const T&obj)
無效Print(const T * obj)
它始終喜歡使用const參考風格。
我閱讀了一些有關“模板參數”推論的內容,但是沒有發現任何問題。
誰能向我解釋一下和/或建議合理的解決方法?
同
template<class T> void Print(const T& obj); // #1
template<class T> void Print(const T* obj); // #2
對於
TemplateTester tester;
tester.Print(print1); // call #1 with T=Printer (#2 is not viable)
tester.Print(&print2); // call #1 with T=Printer* (#1 is a better match than #2)
正如您在(不平凡的) overload_resolution規則中看到的那樣,對於Printer*
,#1比#2更匹配:
Printer*
-> Printer* const &
(完全匹配)
與
Printer*
-> const Printer*
。 (轉換)
有幾種解決方法,例如標簽分發:
class TemplateTester
{
private:
template <typename T>
void Print(const T& obj, std::false_type) {obj.PrintNewline();}
template <typename T>
void Print(const T* obj, std::true_type) {obj->Print(); /*obj->Test();*/}
public:
template <typename T>
void Print(T&& obj) {
Print(obj, std::is_pointer<std::decay_t<T>>{});
}
};
您的問題與模板無關,而與指針語義和函數重載有關。
那是因為在第二種情況下,您要求一個指向const對象的指針。 這意味着指針將具有只讀權限。 通過傳遞非常量對象的引用,您可以傳遞類似具有讀/寫權限的指針值之類的東西-但是,此類指針沒有函數重載。 如果要使用只讀指針,則必須這樣聲明它們:
const T* ptr = ...
T* const ptr = ...
在將它們傳遞給您的函數之前。
請注意,如果它指向const對象,則需要這樣的指針。 但是可以將這樣的指針重新分配給非const對象(但仍僅具有只讀訪問權限)。 特別是這樣的指針不會將您的對象變成const對象-只是通過該指針保護對象免於更改...
解決方法是引入第三個功能:
template<T>
Print(T* obj){
const T* ptr = obj;
Print(ptr);
}
如aschepler所建議。 或將指針函數更改為采用任何指針並使用const引用函數:
template<T>
Print(T* obj){
Print((*obj));
}
但我認為這些解決方案會使您的接口模糊不清,因為僅憑功能簽名對對象進行處理並不明顯。 您的變體不會出現一些問題,迫使您的用戶使用只讀指針。
將一些答案匯總在一起。 我想我理解為什么現在會發生這種情況,以及為什么將指針提升為const指針無法像正常函數調用那樣工作。
如果我正確理解@ Jarod42響應,那是因為模板中的T被評估為Printer*
而不是Printer
! 回想起來,這很有意義。 然后,它將T衰減到作用域塊內的Printer&或Printer *中進行評估,但是在選擇功能簽名后才發生。
這是將所有內容組合在一起的代碼塊。 它包含了一些不同的constness的示例,因為對此早有一些混淆。 感謝大家的貢獻!
#include <iostream>
template <typename T>
void Print(T& obj) { obj.Print(); }
template <typename T>
void Print(T* obj) { obj->Print(); }
template <typename T>
void PrintNewline(const T& obj) { obj.PrintNewline(); }
template <typename T>
void PrintNewline(const T* obj) { obj->PrintNewline(); }
template <typename T>
void BetterPrint(const T& obj, std::false_type) { obj.PrintNewline(); }
template <typename T>
void BetterPrint(const T* obj, std::true_type) { obj->PrintNewline(); }
template <typename T>
void PrintChooser(T&& obj) { BetterPrint(obj, std::is_pointer<std::decay_t<T>>{}); }
class Printer
{
public:
void Print() const { std::cout << m_string.c_str(); }
void PrintNewline() const { std::cout << m_string.c_str() << "\n"; }
void Test() { m_string = "Oops"; } // Not const!
std::string m_string;
};
void Test1(const Printer* val)
{
val->Print();
// val->Test(); // Object type is const Printer.
val = nullptr;
}
void Test2(Printer const* val)
{
val->Print();
// val->Test(); // Object type is const Printer.
val = nullptr;
}
void Test3(Printer* const val)
{
val->Print();
val->Test();
// val = nullptr; // you cannont assign to a value that is const
}
int main()
{
Printer print1, print2;
print1.m_string = "Hello";
print2.m_string = "World";
Print(print1); // Print(T&) as expected.
Print(&print2); // Print(T*) as expected.
PrintNewline(print1); // PrintNewline(const T&) as expected
// PrintNewline(&print1); // PrintNewline(const T&) but expected PrintNewline(const T*) - Won't compile
const Printer* print2Ptr = &print2; // Making user of your API do this is first isn't a happy place.
PrintNewline(print2Ptr); // PrintNewline(const T*) as expected.
PrintChooser(print1);
PrintChooser(&print2); // Happiness
Test1(&print1); // Promotes correctly
Test2(&print1); // Promotes correctly
Test3(&print1); // Promotes correctly
Test1(print2Ptr); // Pointer const matches
Test2(print2Ptr); // Pointer const matches
// Test3(print2Ptr); // Fails to compile, funciton takes a pointer to a non-cost object.
return 0;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.