旧MSVC编译器中std::enable_if结合constexpr方法的重复定义问题如何解决?
旧版MSVC编译
std::enable_if_t结合constexpr函数的问题解决与原因分析 问题场景
当将constexpr函数作为std::enable_if_t的模板参数用于函数模板重载时,旧版MSVC编译器(如19.14)会报错“函数模板已定义”,但G++及新版MSVC(如19.33)可正常编译。示例代码如下:
#include <climits> #include <iostream> #include <type_traits> template <typename NumberType> constexpr auto doBitCountsMatch(const int f_bitCount) -> bool { return (static_cast<int>(CHAR_BIT * sizeof(NumberType)) == f_bitCount); } template <typename NumberType, int bitCount> auto test() -> std::enable_if_t<doBitCountsMatch<NumberType>(bitCount), NumberType> { std::cout << "Bit counts match." << std::endl; return 0; } template <typename NumberType, int bitCount> auto test() -> std::enable_if_t<!doBitCountsMatch<NumberType>(bitCount), NumberType> { std::cout << "Bit counts don't match." << std::endl; return 0; } int main() { int number = 0; test<decltype(number), 32>(); return 0; }
适配旧版MSVC的修改方案
方案1:将constexpr函数改为模板常量表达式
把doBitCountsMatch从函数改为模板变量,让编译器能直接在模板参数阶段解析常量值:
#include <climits> #include <iostream> #include <type_traits> // 改为模板常量表达式 template <typename NumberType, int bitCount> constexpr bool doBitCountsMatch = (static_cast<int>(CHAR_BIT * sizeof(NumberType)) == bitCount); template <typename NumberType, int bitCount> std::enable_if_t<doBitCountsMatch<NumberType, bitCount>, NumberType> test() { std::cout << "Bit counts match." << std::endl; return 0; } template <typename NumberType, int bitCount> std::enable_if_t<!doBitCountsMatch<NumberType, bitCount>, NumberType> test() { std::cout << "Bit counts don't match." << std::endl; return 0; } int main() { int number = 0; test<decltype(number), 32>(); return 0; }
方案2:将enable_if_t移至模板参数列表
把SFINAE约束从返回值位置移到模板参数列表中,旧版MSVC能正确识别不同的模板签名:
#include <climits> #include <iostream> #include <type_traits> template <typename NumberType> constexpr auto doBitCountsMatch(const int f_bitCount) -> bool { return (static_cast<int>(CHAR_BIT * sizeof(NumberType)) == f_bitCount); } // 将enable_if_t作为模板参数的默认值 template <typename NumberType, int bitCount, std::enable_if_t<doBitCountsMatch<NumberType>(bitCount), int> = 0> NumberType test() { std::cout << "Bit counts match." << std::endl; return 0; } template <typename NumberType, int bitCount, std::enable_if_t<!doBitCountsMatch<NumberType>(bitCount), int> = 0> NumberType test() { std::cout << "Bit counts don't match." << std::endl; return 0; } int main() { int number = 0; test<decltype(number), 32>(); return 0; }
旧版MSVC报错原因
旧版MSVC(如19.14)的SFINAE实现存在缺陷:它无法正确解析作为std::enable_if_t模板参数的constexpr函数调用,不能识别两个test模板的enable_if_t约束是互斥的,因此将这两个模板判定为具有相同签名的重复定义。
而新版MSVC和G严格遵循C标准,能够在模板重载决议阶段正确计算constexpr表达式的值,识别两个模板的约束互斥,从而通过编译。
内容的提问来源于stack exchange,提问作者Benjamin Bihler
相关产品推荐
相关产品推荐

