类模板参数推导:非模板推导指南无效是标准规定还是Bug?
1. 这是标准规定还是编译器Bug?
这绝对是标准规定的行为,不是编译器Bug,核心原因出在推导指南的参数类型匹配上:
你第一个推导指南的定义是:
template <std::size_t N> PrettyPrint(char(&)[N]) -> PrettyPrint<const char*>;
但要注意:C++中字符串字面量的类型是const char[N](比如"hello"是const char[6]),而你的推导指南参数是char(&)[N]——这是一个非const的char数组引用,无法匹配到const char[N]类型的字符串字面量(因为非const引用不能绑定到const左值)。所以这个推导指南根本不会被编译器选中,自然无法引导推导到PrettyPrint<const char*>。
而你第二个推导指南:
template <typename T, std::size_t N> PrettyPrint(T(&)[N]) -> PrettyPrint<const T*>;
这里的模板参数T会被编译器推导为const char(因为传入的是const char[N]),所以参数类型T(&)[N]就变成了const char(&)[N],完美匹配字符串字面量的类型。此时推导指南生效,引导编译器推导出PrettyPrint<const char*>,从而触发你写的全特化版本,输出符合预期。
2. 如何将推导指南限制为仅适用于char类型?
如果你想让推导指南只对char或const char数组生效,有几种方式,取决于你使用的C++版本:
方式1:直接针对char和const char数组写推导指南(C++17及以上)
最简单直接的方式是分别写两个推导指南,覆盖非const和const的char数组:
// 匹配非const char数组 template <std::size_t N> PrettyPrint(char(&)[N]) -> PrettyPrint<const char*>; // 匹配const char数组(比如字符串字面量) template <std::size_t N> PrettyPrint(const char(&)[N]) -> PrettyPrint<const char*>;
方式2:用SFINAE限制(C++17及以上)
如果你想写一个通用的推导指南,通过SFINAE过滤掉非char类型的数组,可以用std::enable_if:
#include <type_traits> template <typename T, std::size_t N, typename = std::enable_if_t<std::is_same_v<std::remove_const_t<T>, char>>> PrettyPrint(T(&)[N]) -> PrettyPrint<const char*>;
这里std::remove_const_t<T>会去掉T的const修饰,然后判断是否等于char,只有满足条件时推导指南才会生效。
方式3:用C++20 Concepts(更优雅)
如果你使用C++20,可以用Concepts写出可读性更好的限制:
#include <concepts> template <typename T, std::size_t N> requires std::same_as<std::remove_const_t<T>, char> PrettyPrint(T(&)[N]) -> PrettyPrint<const char*>;
这个写法和SFINAE的效果一致,但代码更清晰,一眼就能看出我们只接受char相关的数组类型。
内容的提问来源于stack exchange,提问作者PiotrNycz

