编译期检测std::array类型时const修饰导致断言失败的原因咨询
我尝试在编译期检测变量是否为std::array类型,参考了相关实现,但遇到const修饰的std::array(包括constexpr变量)时,多个静态断言失败,示例代码如下:
#include <array> template<typename> struct is_std_array : std::false_type {}; template<typename T, std::size_t N> struct is_std_array<std::array<T,N>> : std::true_type {}; std::array<int, 5> nonConstArray = {}; const std::array<int, 5> constArray = {}; constexpr std::array<int, 5> constExprArray = {}; // succeeds - makes sense static_assert(is_std_array<std::array<int, 5>>::value, "Assertion 1."); // also succeeds static_assert(is_std_array<decltype(nonConstArray)>::value, "Assertion 2."); // fails - ??? (how does remove_const affect this when this passed before?) static_assert(is_std_array<std::remove_const<decltype(nonConstArray)>>::value, "Assertion 3."); // fails - ??? - maybe because type is const? static_assert(is_std_array<decltype(constArray)>::value, "Assertion 4."); // fails - ??? - shouldn't the type with const removed work? static_assert(is_std_array<std::remove_const<decltype(constArray)>>::value, "Assertion 5."); // fails static_assert(is_std_array<decltype(constExprArray)>::value, "Assertion 6."); // fails static_assert(is_std_array<std::remove_const<decltype(constExprArray)>>::value, "Assertion 7.");
我认为断言3、5、7应该通过,断言4、6也可能通过,想请教这些断言失败的原因。
断言失败原因分析
断言4、6失败的原因
你的is_std_array模板特化仅匹配无const/volatile限定的std::array<T,N>,而decltype(constArray)的实际类型是const std::array<int,5>,constexpr修饰的constExprArray类型同样是const std::array<int,5>(constexpr变量默认带const属性)。这两个带const限定的类型和模板特化的目标类型不匹配,因此断言4、6会失败。
断言3、5、7失败的原因
这是误用std::remove_const的典型问题:std::remove_const是一个模板类,你直接将std::remove_const<...>传入is_std_array,实际上传入的是模板类本身的类型,而非它的type成员所代表的「移除const后的目标类型」。
正确用法需要访问std::remove_const的type成员(C++14及以上可用std::remove_const_t简化),比如断言3应修改为:
static_assert(is_std_array<std::remove_const<decltype(nonConstArray)>::type>::value, "Assertion 3.");
同理,断言5、7也需要加上::type或改用std::remove_const_t,才能正确匹配is_std_array的特化版本。
优化后的检测模板
如果想让is_std_array直接支持所有带cv(const/volatile)限定的std::array,可以用更简洁的方式实现:
#include <array> #include <type_traits> template<typename T> struct is_std_array_helper : std::false_type {}; template<typename T, std::size_t N> struct is_std_array_helper<std::array<T,N>> : std::true_type {}; // 先移除类型的cv限定,再交给辅助模板判断 template<typename T> struct is_std_array : is_std_array_helper<std::remove_cv_t<T>> {};
这个版本通过std::remove_cv_t统一移除类型的const/volatile限定,再由辅助模板判断是否为std::array特化,代码更简洁且覆盖所有cv情况。
内容的提问来源于stack exchange,提问作者alex

