数组转发引用的函数模板正确定义及左值处理问题
当调用do_something_prob(arr, 2)时,arr是左值,根据C++转发引用的推导规则,模板参数T会被推导为std::array<int,3>&(左值引用类型)。
问题出在第二个参数的类型typename T::value_type上:引用类型没有嵌套的value_type成员。编译器尝试访问std::array<int,3>&::value_type,但引用不是类/结构体/联合体类型,自然找不到这个嵌套类型,因此触发编译错误,也就是你看到的'std::array<int, 3>&' is not a class, struct, or union type。
反观do_something_no_prob的写法,它把元素类型ElemT的默认值定义为:
std::enable_if_t<is_array<std::decay_t<T>>::value, typename T::value_type>
这里的std::decay_t<T>会去除T的引用属性(如果是左值引用,std::decay_t<std::array<int,3>&>就是std::array<int,3>),再通过去除引用后的类型获取value_type,同时用enable_if做了SFINAE限制,确保只有数组类型能匹配这个模板。
do_something_prob的SFINAE条件用std::decay_t<T>判断是否为数组是没问题的,问题完全出在第二个参数的类型推导上——没有处理T是引用类型的情况。
如果要修复do_something_prob,可以把第二个参数的类型改成typename std::decay_t<T>::value_type,这样不管T是左值引用还是右值引用,都能正确拿到元素类型:
template <typename T> std::enable_if_t<is_array<std::decay_t<T>>::value> do_something_prob(T&& arr, typename std::decay_t<T>::value_type mul) { for (auto && v : arr) std::cout << v * mul << '\n'; }
修改后,传入左值arr时,std::decay_t<T>是std::array<int,3>,typename std::decay_t<T>::value_type就是int,就能正常编译了。
内容的提问来源于stack exchange,提问作者deponovo

