[英]Template Return c++
我正在嘗試基於全局變量DOF
在兩個 class 對象之間切換。 想法是使用模板 class 更改返回類型。 但是 main() 中的第一行有一個編譯時錯誤template argument deduction/substitution failed
。 你能幫我理解問題並解決它嗎?有沒有更好的方法呢? 任何建議和幫助表示贊賞。 提前致謝。
#include <iostream>
#include <string>
class MM
{
public:
MM(){}
std::string printName()
{
return "MM";
}
};
class MM2
{
public:
MM2(){}
std::string printName()
{
return "MM2";
}
};
using namespace std;
const unsigned short int DOF = 7;
MM* obj = nullptr;
MM2* obj2 = nullptr;
template<class T>
T getClass()
{
if(DOF==7)
{
if(obj == nullptr)
{
obj = new MM();
}
return obj;
}
else if(DOF == 6)
{
if(obj2 == nullptr)
{
obj2 = new MM2();
}
return obj2;
}
}
int main()
{
getClass()->printName();
//std::cout << "class name " << getClass()->printName() << std::endl;
return 0;
}
這不是模板在 C++ 中的工作方式。 模板參數的類型必須在編譯時知道,並且不能在運行時更改。
您在示例掃描中嘗試實現的模式很容易通過虛擬函數完成:使MM
和MM2
具有共同的基礎 class 並使printName
成為虛擬 function。 雖然我們在這里:不要使用手動 memory 管理,即不要使用顯式的新建/刪除。 使用像unique_ptr
這樣的智能指針。
其他選項是std::any
和std:: variant
但我不會推薦它們,除非你有一個非常特殊的用例。
對於您的簡單示例,一個選項可能是返回 function 指針或std::function
。 這將適用於您的示例,因為您的類是無狀態的,但我懷疑您的真實類具有 state 或您希望訪問的更多方法,在這種情況下您不應該嘗試這樣做。
如果您可以使用 C++17(如果您不能使用,那就太可惜了),如果您稍微切換一下,您就可以做到這一點。
首先,使用模板參數來確定getClass
的作用並使用if constexpr
而不是普通的if
:
template<int N>
auto getClass()
{
if constexpr (N == 7)
{
if(obj == nullptr)
{
obj = new MM();
}
return obj;
}
else if constexpr (N == 6)
{
if(obj2 == nullptr)
{
obj2 = new MM2();
}
return obj2;
}
}
然后像這樣調用這個模板:
std::cout << "class name " << getClass <DOF> ()->printName() << std::endl;
雜項說明:
通過getClass
的所有路徑都應該返回一個值。
您通過調用new
而不是調用delete
來泄漏 memory 。 有更好的選擇。
編輯:這是使用 SFINAE 的 C++11 解決方案:
template<int N, typename std::enable_if<N == 7, int>::type = 0>
MM *getClass()
{
if(obj == nullptr)
{
obj = new MM();
}
return obj;
}
template<int N, typename std::enable_if<N == 6, int>::type = 0>
MM2 *getClass()
{
if(obj2 == nullptr)
{
obj2 = new MM2();
}
return obj2;
}
然后你仍然可以這樣做:
std::cout << "class name " << getClass <DOF> ()->printName() << std::endl;
這是你可以嘗試的。 正如其他人所說,模板僅在編譯時起作用:如果您想稍后在運行時動態更改類型,那么多態是 go 的方法。 您可以使用一種“PIMPL”設計在 MM 和 MM2 類之上有效地“插入”一個基礎 class。 基礎 class 包含純虛函數,用於您需要訪問的 MM 和 MM2 的所有常用函數(例如,本例中的 printName())。
#include <iostream>
#include <memory>
#include <string>
class MM
{
public:
MM() {}
std::string printName()
{
return "MM";
}
};
class MM2
{
public:
MM2() {}
std::string printName()
{
return "MM2";
}
};
class MMBase
{
public:
virtual std::string printName() = 0;
virtual ~MMBase() {}
};
//Templated wrapper for each MM class type, deriving from abstract MMBase
template<class T>
class MMWrap : public MMBase
{
std::unique_ptr<T> _impl;
public:
MMWrap() : _impl(nullptr)
{
_impl = std::make_unique<T>();
}
//Pass function call to _impl pointer
std::string printName()
{
return _impl->printName();
}
};
class MMFactory
{
public:
enum MMType {TypeMM2=6,TypeMM};
static MMType _type;
static std::unique_ptr<MMBase> getMM()
{
if (_type == TypeMM) return std::unique_ptr<MMBase>(new MMWrap<MM>());
if (_type == TypeMM2) return std::unique_ptr<MMBase>(new MMWrap<MM2>());
return nullptr; //Avoids compiler warning about not all paths return value
}
};
//Initialize static member to which default MM type is required
MMFactory::MMType MMFactory::_type = MMFactory::TypeMM;
int main()
{
std::cout<< MMFactory::getMM()->printName() << std::endl;
MMFactory::_type = MMFactory::TypeMM2;
std::cout << MMFactory::getMM()->printName() << std::endl;
}
我已經放入了一個模板化的包裝器 class,但這可能需要根據 MM/MM2 構造函數需要的參數進行修改。 此外,包裝的指針是在構造函數中創建的(如果它們拋出則可能存在問題):這些可以移動到惰性求值 model,使 _impl 可變。 我不知道以后如何使用 MM/MM2:如果它們具有引用其他 MM 類型的函數,則可能需要做更多的工作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.