C++中能否用方法返回值作为另一方法的模板参数?
简要说明
我想知道能不能在调用类的模板方法时,把另一个方法的返回值作为模板参数传入。比如有如下类:
//myClass.hh class myClass { private: public: int getType(); template<int i> someReturnType cast(); };
希望实现这样的调用:
//main.cpp myClass obj; obj.cast<obj.getType()>();
详细说明
我正在实现一个能存储不同数据类型节点的链表,目前节点部分已经完成。
为了让链表类能容纳不同类型的节点(存储不同数据类型),我这样定义节点类:
//Node.hh class voidNode { private: public: const TypeID _type; NodeNumber _nodeNumber; voidNode *_next; explicit voidNode(const TypeID id) : _type(id), _next(nullptr), _nodeNumber(0) { }; virtual ~voidNode() { }; virtual const TypeID getNodeType() = 0; }; template<class dataType> class Node : public voidNode { private: dataType _data; public: explicit Node() { } explicit Node(const dataType &data, const NodeNumber &n = 0) : _data(data), voidNode(GetTypeID<dataType>::_typeID) { _nodeNumber = n; }; virtual ~Node() { }; void setData(const dataType &data) { _data = data; } dataType getData() { return _data; }; NodeNumber getNodeNumber() { return _nodeNumber; } const TypeID getNodeType() override { return _type; } friend class singleList; }; template<std::size_t N> Node(char const (&)[N], const NodeNumber (&)) -> Node<char const*>;
目前这个实现可以正常工作,我能像这样实例化节点:
//main.cpp char *cStyleString = "c style string"; const char *constantCStyleString = "constant c style string"; const char ff = 'A'; float dec = 98.99; uint32_t uintN{987}; int32_t intN{-97}; bool BoolT{true}; Node node1(uintN); Node node2(intN); Node node3(BoolT); Node node4((uint)87); Node node5(-999); Node node6(false); Node node7('c'); Node node8("hola",0); Node node9(cStyleString, 0); Node node10(ff, 0); Node node11(dec, 0); std::cout << node1.getData() << std::endl; std::cout << node2.getData() << std::endl; std::cout << node3.getData() << std::endl; std::cout << node4.getData() << std::endl; std::cout << node5.getData() << std::endl; std::cout << node6.getData() << std::endl; std::cout << node7.getData() << std::endl; std::cout << node8.getData() << std::endl; std::cout << node9.getData() << std::endl; std::cout << node10.getData() << std::endl; std::cout << node11.getData() << std::endl;
得到的输出如下:
在链表类中,我通过基类存储节点,因此提供了将基类转换为对应派生类的方法:
//list.hh class singleList { private: //Base class for nodes. voidNode *_start{nullptr}; voidNode *_current{nullptr}; voidNode *_last{nullptr}; public: template<class ...Args> explicit singleList(const Args &...args); ~singleList(); /**this method returns the base class ptr casted *to one of its derived classes, depending on *the template parameter "i". I.e, i = 0 returns Node<bool>*, *i = 1 returns Node<char>*, etc. */ template<int i> Node<typename getType<i, _types>::_type> *nodeCast(voidNode *node) { return reinterpret_cast<Node<typename getType<i, _types>::_type>*>(node); } //Same method as above, but takes "TypeID" type instead of int template<const TypeID ID> Node<typename getType<static_cast<int>(ID), _types>::_type> *getElement(voidNode *node) { return getType<static_cast<int>(ID)>(node); } };
我的问题是:每个节点都有标识其数据类型的成员,我想这样调用getElement方法:
//main.cpp singleList mylist(BoolT, intN, uintN, dec, constantCStyleString); mylist.getElement<mylist._current->getNodeType()>(mylist._current);
但出现了编译错误:
[build] /home/inumaki/Development/cppWorkspace/secondProject/main.cpp:40:52: error: the value of ‘mylist’ is not usable in a constant expression [build] 40 | mylist.getElement<mylist._current->getNodeType()>(mylist._current); [build] | ^ [build] /home/inumaki/Development/cppWorkspace/secondProject/main.cpp:39:16: note: ‘mylist’ was not declared ‘constexpr’ [build] 39 | singleList mylist(BoolT, intN, uintN, dec, constantCStyleString); [build] | ^~~~~~ [build] /home/inumaki/Development/cppWorkspace/secondProject/main.cpp:40:54: error: no matching function for call to ‘singleList::getElement<mylist.singleList::_current->voidNode::getNodeType()>(voidNode*&)’ [build] 40 | mylist.getElement<mylist._current->getNodeType()>(mylist._current); [build] | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~ [build] In file included from /home/inumaki/Development/cppWorkspace/secondProject/main.cpp:2: [build] /home/inumaki/Development/cppWorkspace/secondProject/singleList.hh:58:70: note: candidate: ‘template<TypeID ID> Node<typename getType<static_cast<int>(ID), TypeList<void, bool, char, char*, unsigned char, signed char, short unsigned int, short int, unsigned int, int, float> >::_type>* singleList::getElement(voidNode*)’ [build] 58 | Node<typename getType<static_cast<int>(ID), _types>::_type> *getElement(voidNode *node) [build] | ^~~~~~~~~~ [build] /home/inumaki/Development/cppWorkspace/secondProject/singleList.hh:58:70: note: template argument deduction/substitution failed: [build] /home/inumaki/Development/cppWorkspace/secondProject/main.cpp:40:54: error: the value of ‘mylist’ is not usable in a constant expression [build] 40 | mylist.getElement<mylist._current->getNodeType()>(mylist._current); [build] | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~ [build] /home/inumaki/Development/cppWorkspace/secondProject/main.cpp:39:16: note: ‘mylist’ was not declared ‘constexpr’ [build] 39 | singleList mylist(BoolT, intN, uintN, dec, constantCStyleString); [build] | ^~~~~~ [build] /home/inumaki/Development/cppWorkspace/secondProject/main.cpp:40:51: note: in template argument for type ‘TypeID’ [build] 40 | mylist.getElement<mylist._current->getNodeType()>(mylist._current); [build] | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~
我试过把表达式改成constexpr,但引发了更多错误,我觉得这不是合适的方案,暂时不展开,需要的话可以补充细节。
解决方案
模板参数必须是编译期常量,而mylist._current->getNodeType()是运行时才能确定的值,所以直接作为模板参数传入是行不通的,这也是编译器报错的核心原因。
针对你的链表场景,有三种常见的改进方案:
1. 利用虚方法封装数据访问(推荐)
既然已经有了voidNode基类,可以在基类中定义纯虚方法来处理数据,避免强制类型转换。比如:
class voidNode { // ... 现有成员 ... virtual void printData() const = 0; // 可以根据需求添加其他操作,比如保存、转换等 }; template<class dataType> class Node : public voidNode { // ... 现有成员 ... void printData() const override { std::cout << _data << std::endl; } };
这样在链表中直接调用node->printData()即可,无需关心具体类型,完全利用多态处理,代码更安全简洁。
2. 运行时分支 + 模板特化
如果必须保留类型转换的逻辑,可以通过运行时的TypeID分支,手动调用对应模板实例:
class singleList { // ... 现有成员 ... voidNode* getCurrentNode() { return _current; } // 新增非模板方法,根据TypeID分发 template<typename Func> void processCurrentNode(Func&& func) { TypeID id = _current->getNodeType(); switch(id) { case GetTypeID<bool>::_typeID: func(static_cast<Node<bool>*>(_current)); break; case GetTypeID<char>::_typeID: func(static_cast<Node<char>*>(_current)); break; case GetTypeID<int>::_typeID: func(static_cast<Node<int>*>(_current)); break; // ... 其他类型分支 ... default: throw std::runtime_error("Unknown node type"); } } };
调用时可以这样用:
mylist.processCurrentNode([](auto* node) { std::cout << node->getData() << std::endl; });
这种方式需要维护所有类型的分支,但能在运行时动态处理不同节点类型。
3. 改用std::variant(C++17及以上)
如果可以重构节点设计,用std::variant存储不同类型的数据,链表节点直接持有std::variant,不需要基类和派生类:
using ValueType = std::variant<bool, char, int, float, const char*>; class Node { private: ValueType _data; NodeNumber _nodeNumber; Node* _next; public: // ... 构造函数和成员方法 ... void printData() const { std::visit([](const auto& val) { std::cout << val << std::endl; }, _data); } };
这种方式更简洁,利用标准库的变体类型处理多态,不需要手动管理继承和类型转换。
内容的提问来源于stack exchange,提问作者Juan_David

