以下代码可以正常工作并打印:
你好
世界末日
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;
}
但是,如果您将打印功能更改为
无效打印(const T&obj)
无效打印(const T * obj)
它总是更喜欢const引用风格。
我读了一些有关模板参数推论的内容,但我没有想到。
谁能向我解释一下和/或建议合理的解决方法?
答案 0 :(得分:4)
使用
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>>{});
}
};
答案 1 :(得分:2)
您的问题与模板无关,而与指针语义和函数重载有关。
那是因为在第二种情况下,您要求一个指向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));
}
但是我认为这些变通方法会使您的接口模糊不清,因为仅凭功能签名对对象进行处理并不明显。您的变体不会出现一些问题,迫使您的用户使用只读指针。
答案 2 :(得分:0)
将一些答案汇总在一起。我想我理解为什么现在会发生这种情况,以及为什么将指针升级为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;
}