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

C++可变参数模板解包调用read生成tuple遇编译器内部错误

问题:用折叠表达式实现tuple类型的read方法触发编译器内部错误

我想通过可变参数模板解包,为每个参数调用read<>()方法,再收集结果创建新的std::tuple对象。学习C++17折叠表达式后,我写了这段代码:

template<typename ...U>
std::tuple<U...> read_impl(std::tuple<U...>* arg) {
    // 该参数用于函数重载,作为偏特化的替代方案
    using T = std::tuple<U...>;
    return T(read<U>(), ...);
}

但这段代码触发了MSVC内部编译错误(Fatal Error C1001)。

预期用法

std::tuple<int, float, double> tup = read_impl(static_cast<std::tuple<int, float, double>*>(0));
// 等价于
std::tuple<int, float, double> tup = std::make_tuple(read<int>(), read<float>(), read<double>());

当前临时解决方案

目前我只能手动特化不同参数数量的read_impl函数,这种方式冗长且不优雅:

template<typename U1>
std::tuple<U1> read_impl(std::tuple<U1>* arg) {
    using T = std::remove_pointer<decltype(arg)>::type;
    return T(read<U1>());
}
template<typename U1, typename U2>
std::tuple<U1, U2> read_impl(std::tuple<U1, U2>* arg) {
    using T = std::remove_pointer<decltype(arg)>::type;
    return T(read<U1>(), read<U2>());
}
template<typename U1, typename U2, typename U3>
std::tuple<U1, U2, U3> read_impl(std::tuple<U1, U2, U3>* arg) {
    using T = std::remove_pointer<decltype(arg)>::type;
    return T(read<U1>(), read<U2>(), read<U3>());
}
...

最小可复现案例

#include <tuple>

class Reader {
public:
    template<typename T>
    T read() {
        return read_impl(static_cast<T*>(0));
    }
    int read_impl(int*) {
        return 1;
    }
    float read_impl(float*) {
        return 0.5f;
    }
    template<typename ...U>
    std::tuple<U...> read_impl(std::tuple<U...>* arg) {
        using T = std::tuple<U...>;
        return T(read<U>(), ...);
    }

    static void test() {
        Reader reader;
        auto result = reader.read<std::tuple<int, float>>();
        assert(std::get<0>(result) == 1);
        assert(std::get<1>(result) == 0.5f);
    }
};

int main() {
    Reader::test();
    return 0;
}

对评论的回复

关于为什么使用tuple裸指针且不使用arg

因为直接对返回值特化通用函数会编译失败,示例如下:

#include <exception>
#include <assert.h>

class Reader {
public:
    template<typename T>
    T f() {
        throw std::exception();
    }

    template<>
    int f() {
        return 1;
    }

    template<>
    float f() {
        return 0.5f;
    }

    static void test_f() {
        Reader reader;
        auto result = reader.f<int>();
        assert(result == 1);
    }
};

int main() {
    Reader::test_f();
    return 0;
}

补充完整的Reader类示例

我的Reader类需要支持读取复杂类型,比如reader.read<std::unordered_map<int, std::vector<std::string>>>(),完整代码如下:

#include "pch.h"
#include <unordered_map>
#include <unordered_set>
#include <tuple>
#include <string>
#include <optional>

class Reader {
public:
    template<typename T>
    T read() {
        return read_impl(static_cast<T*>(0));
    }

    template<typename T>
    T read_impl(T*) {
        T::deserialize(*this);
    }
    uint64_t read_impl(uint64_t*) {
        return 1; // 实际场景中不会是常量
    }
    uint32_t read_impl(uint32_t*) {
        return 1; // 实际场景中不会是常量
    }
    int read_impl(int*) {
        return 1; // 实际场景中不会是常量
    }
    float read_impl(float*) {
        return .5f; // 实际场景中不会是常量
    }
    bool read_impl(bool*) {
        return true; // 实际场景中不会是常量
    }
    std::string read_impl(std::string*) {
        return "readString"; // 实际场景中不会是常量
    }
    template<typename T>
    std::vector<T> read_impl(std::vector<T>*) {
        size_t size = read<uint64_t>();
        std::vector<T> r;
        r.reserve(size);
        for (size_t i = 0; i < size; i++) {
            r.push_back(read<T>());
        }
        return r;
    }
    template<typename TKey, typename TValue>
    std::unordered_map<TKey, TValue> read_impl(std::unordered_map<TKey, TValue>*) {
        size_t size = read<uint64_t>();
        std::unordered_map<TKey, TValue> r;
        r.reserve(size);
        for (size_t i = 0; i < size; i++) {
            TKey k = read<TKey>();
            TValue v = read<TValue>();
            r[k] = v;
        }
        return r;
    }
    template<typename T>
    std::unordered_set<T> read_impl(std::unordered_set<T>*) {
        size_t size = read<uint64_t>();
        std::unordered_set<T> r;
        r.reserve(size);
        for (size_t i = 0; i < size; i++) {
            r.insert(read<T>());
        }
        return r;
    }
    template<typename ...U>
    std::tuple<U...> read_impl(std::tuple<U...>* arg) {
        using T = std::tuple<U...>;
        return T{ read<U>() ... };
    }
    template<typename T>
    std::optional<T> read_impl(std::optional<T>* t) {
        bool hasValue = read<bool>();
        std::optional<T> r;
        if (hasValue) {
            r = read<T>();
        }
        return r;
    }

    static void test() {
        Reader reader;
        auto result = reader.read<std::tuple<int, float>>();
        assert(std::get<0>(result) == 1);
        assert(std::get<1>(result) == 0.5f);
        auto result2 = reader.read<std::unordered_map<int, std::vector<std::string>>>();
        assert(result2[1] == std::vector<std::string>{"readString"});
    }
};

int main() {
    Reader::test();
    return 0;
}

感谢反馈

感谢@HolyBlackCat 和 @Jarod42 指出代码中的求值顺序问题。


内容的提问来源于Stack Exchange,提问作者felixh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 20:20:36