C++17下MSVC中constexpr类成员赋值函数指针报错问题
解决MSVC下constexpr函数指针初始化的兼容问题
问题背景
基于数组库构建上层类,通过函数指针参数化分发逻辑以获取数组标量类型,该代码在GCC与Clang下编译正常,但MSVC抛出两个错误:
error C2440: 'initializing': cannot convert from 'ArrowStringView (__cdecl *)(const ArrowArrayView *,int64_t)' to 'const StringArray::GetFuncPtrT'- 尝试C风格转换时,出现
a non-constant (sub-)expression was encountered错误
相关代码结构如下:
数组类定义
class AnArray { ... }; class IntArray : public AnArray { public: using ArrowScalarT = int64_t; using GetFuncPtrT = ArrowScalarT (*)(const struct ArrowArrayView *, int64_t); static constexpr GetFuncPtrT ArrowGetFunc = &ArrowArrayViewGetIntUnsafe; ... }; class StringArray : public AnArray { public: using ArrowScalarT = struct ArrowStringView; using GetFuncPtrT = ArrowScalarT (*)(const struct ArrowArrayView *, int64_t); static constexpr GetFuncPtrT ArrowGetFunc = &ArrowArrayViewGetStringUnsafe; ... };
模板算法
template <typename T> void SomeAlgorithm(const T& obj) { struct ArrowArrayView* array_view = ...; int64_t index = ...; const auto value = T::ArrowGetFunc(array_view, index); ... }
第三方库函数声明
static inline struct ArrowStringView ArrowArrayViewGetStringUnsafe( const struct ArrowArrayView* array_view, int64_t i) { } static inline int64_t ArrowArrayViewGetIntUnsafe( const struct ArrowArrayView* array_view,int64_t i) { }
解决方案
问题核心是MSVC对C/C++函数链接类型、函数指针类型匹配的检查更严格,尤其是在constexpr上下文。以下是三种可行的解决方式:
方法1:统一函数指针的链接类型声明
在C++代码中为第三方C函数添加extern "C"声明,确保函数指针类型完全匹配:
extern "C" { struct ArrowStringView ArrowArrayViewGetStringUnsafe( const struct ArrowArrayView* array_view, int64_t i); int64_t ArrowArrayViewGetIntUnsafe( const struct ArrowArrayView* array_view,int64_t i); }
这样MSVC会将这些函数识别为C链接类型,constexpr初始化可正常通过。
方法2:使用constexpr包装函数作为中间层
若无法修改第三方函数声明,可在数组类中添加constexpr静态包装函数,间接调用第三方函数:
class StringArray : public AnArray { public: using ArrowScalarT = struct ArrowStringView; using GetFuncPtrT = ArrowScalarT (*)(const struct ArrowArrayView *, int64_t); static constexpr ArrowScalarT GetWrapper(const struct ArrowArrayView* av, int64_t idx) { return ArrowArrayViewGetStringUnsafe(av, idx); } static constexpr GetFuncPtrT ArrowGetFunc = &GetWrapper; ... }; class IntArray : public AnArray { public: using ArrowScalarT = int64_t; using GetFuncPtrT = ArrowScalarT (*)(const struct ArrowArrayView *, int64_t); static constexpr ArrowScalarT GetWrapper(const struct ArrowArrayView* av, int64_t idx) { return ArrowArrayViewGetIntUnsafe(av, idx); } static constexpr GetFuncPtrT ArrowGetFunc = &GetWrapper; ... };
通过C++ constexpr包装函数作为桥梁,规避了C链接函数指针与C++函数指针类型的兼容性问题,同时保留constexpr特性。
方法3:使用std::integral_constant封装函数指针
借助标准库的std::integral_constant封装函数指针,MSVC对该方式的constexpr支持更友好:
#include <type_traits> class IntArray : public AnArray { public: using ArrowScalarT = int64_t; using GetFuncPtrT = ArrowScalarT (*)(const struct ArrowArrayView *, int64_t); using ArrowGetFunc = std::integral_constant<GetFuncPtrT, &ArrowArrayViewGetIntUnsafe>; ... }; class StringArray : public AnArray { public: using ArrowScalarT = struct ArrowStringView; using GetFuncPtrT = ArrowScalarT (*)(const struct ArrowArrayView *, int64_t); using ArrowGetFunc = std::integral_constant<GetFuncPtrT, &ArrowArrayViewGetStringUnsafe>; ... };
模板算法中的调用逻辑需修改为:
template <typename T> void SomeAlgorithm(const T& obj) { struct ArrowArrayView* array_view = ...; int64_t index = ...; const auto value = T::ArrowGetFunc::value(array_view, index); ... }
内容的提问来源于stack exchange,提问作者Will Ayd
相关产品推荐
相关产品推荐

