You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 10:40:37