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

如何避免C++函数意外陷入递归?以字符串重载函数为例

问题分析

现有get_val_from_db函数提供了针对signed char和unsigned char的重载,同时有一个模板重载用于将std::string类型的key转换为const char*。但当传入char类型的参数时,由于无法匹配到前两个非模板重载,编译器会选择模板版本。而模板内部调用get_val_from_db(key.c_str(), value)时,const char*会被隐式转换为std::string,导致模板递归调用,最终引发栈溢出。

需要在保留std::string转const char*功能的前提下,让不支持的类型触发编译错误而非递归,使用C++17编译器。

原代码如下:

#include <iostream>

void get_val_from_db(const char* key, signed char& value)
{
    std::cout << "Getting value from db with signed char!\n";
}

void get_val_from_db(const char* key, unsigned char& value)
{
    std::cout << "Getting value from db with unsigned char!\n";
}

// overload to convert first argument from std::string to const char*
template <typename T>
void get_val_from_db(const std::string& key, T& value)
{
    get_val_from_db(key.c_str(), value);
}


int main()
{
    unsigned char my_value_unsigned = 0; // OK!
    get_val_from_db("my key", my_value_unsigned);

    signed char my_value_signed = 0; // OK!
    get_val_from_db("my key", my_value_signed);
    
    char my_value_char = 0; // NOK! Can't convert! Becomes recursive!
    get_val_from_db("my key", my_value_char);

    // Can't remove the std::string conversion altogether because these needs to keep working
    std::string key = "my key";
    get_val_from_db(key, my_value_signed);
    get_val_from_db(key, my_value_unsigned);

}
解决方案

我们可以通过C++17的约束机制或SFINAE特性,确保模板重载仅在存在对应的const char*版本重载时才生效,避免递归调用并触发编译错误。

方法1:使用requires子句(C++17及以上)

通过requires子句显式要求存在get_val_from_db(const char*, T&)的有效重载,这样当T是不支持的类型(如char)时,该模板不会参与重载解析,编译器会直接报错找不到匹配的函数:

#include <iostream>
#include <string>

void get_val_from_db(const char* key, signed char& value)
{
    std::cout << "Getting value from db with signed char!\n";
}

void get_val_from_db(const char* key, unsigned char& value)
{
    std::cout << "Getting value from db with unsigned char!\n";
}

// 仅当存在对应的const char*版本重载时,该模板才生效
template <typename T>
requires requires(const char* k, T& v) { get_val_from_db(k, v); }
void get_val_from_db(const std::string& key, T& value)
{
    get_val_from_db(key.c_str(), value);
}

int main()
{
    unsigned char my_value_unsigned = 0;
    get_val_from_db("my key", my_value_unsigned); // OK

    signed char my_value_signed = 0;
    get_val_from_db("my key", my_value_signed); // OK
    
    char my_value_char = 0;
    // get_val_from_db("my key", my_value_char); // 编译错误:无匹配函数

    std::string key = "my key";
    get_val_from_db(key, my_value_signed); // OK
    get_val_from_db(key, my_value_unsigned); // OK
}

方法2:使用SFINAE与decltype

通过返回类型的decltype检测get_val_from_db(const char*, T&)是否可行,同样可以排除不支持的类型:

#include <iostream>
#include <string>

void get_val_from_db(const char* key, signed char& value)
{
    std::cout << "Getting value from db with signed char!\n";
}

void get_val_from_db(const char* key, unsigned char& value)
{
    std::cout << "Getting value from db with unsigned char!\n";
}

// SFINAE:仅当get_val_from_db(const char*, T&)有效时,模板才被实例化
template <typename T>
auto get_val_from_db(const std::string& key, T& value) -> decltype(get_val_from_db(std::declval<const char*>(), value))
{
    get_val_from_db(key.c_str(), value);
}

int main()
{
    // 同方法1的测试代码
}

方法3:使用static_assert显式检查类型

如果希望直接明确支持的类型,可以在模板内使用static_assert,直接拒绝非支持类型的实例化:

#include <iostream>
#include <string>
#include <type_traits>

void get_val_from_db(const char* key, signed char& value)
{
    std::cout << "Getting value from db with signed char!\n";
}

void get_val_from_db(const char* key, unsigned char& value)
{
    std::cout << "Getting value from db with unsigned char!\n";
}

template <typename T>
void get_val_from_db(const std::string& key, T& value)
{
    // 仅允许signed char或unsigned char类型
    static_assert(std::is_same_v<T, signed char> || std::is_same_v<T, unsigned char>,
                  "get_val_from_db only supports signed char or unsigned char as value type");
    get_val_from_db(key.c_str(), value);
}

int main()
{
    // 同方法1的测试代码
}

这种方式的缺点是后续添加新的支持类型时,需要手动更新static_assert的条件,而前两种方法会自动适配新的const char*重载。


内容的提问来源于stack exchange,提问作者Henrique Jung

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 01:47:49