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
相关产品推荐
相关产品推荐

