C++元编程:模板中using别名编译失败,typedef却正常?
typedef编译正常,using别名触发编译错误的原因与解决方法
在以下代码中,nth_type的特化里使用typedef声明type成员时编译通过,但替换为using别名时,GCC和Clang均报错提示目标结构体中不存在type成员。
错误信息
在替换'template using nth_type_t = typename nth_type::type [with long unsigned int I = 1; T = type_list<int, float, double>]'时: :47:36: 从此处要求 :41:7: 错误:'struct nth_type<1, type_list<int, float, double>>'中没有名为'type'的类型 41 | using nth_type_t = typename nth_type<I, T>::type; | ^~~~~~~~~~ 在替换'template using nth_type_t = typename nth_type::type [with long unsigned int I = 2; T = type_list<int, float, double>]'时: :48:36: 从此处要求 :41:7: 错误:'struct nth_type<2, type_list<int, float, double>>'中没有名为'type'的类型
测试代码
#include <type_traits> #include <utility> template<typename... Ts> struct composition : Ts... { using Ts::operator()...; }; template<typename... Ts> composition(Ts...) -> composition<Ts...>; template<typename T, template<typename...> typename Tmpl> concept is_template = decltype(composition { []<typename ...U>(const Tmpl<U...> &) { return std::true_type{}; }, [](const auto &) { return std::false_type{}; } }(std::declval<T>()))::value; template<typename... Ts> struct type_list {}; template <std::size_t I, typename T> struct indexed { using type = T; }; template <typename Is, typename ...Ts> struct indexer; template <std::size_t ...Is, typename ...Ts> struct indexer<std::index_sequence<Is...>, Ts...> : indexed<Is, Ts>... {}; template<typename ...Ts> auto make_indexer() { return indexer<std::index_sequence_for<Ts...>, Ts...>{}; } template <std::size_t I, typename ...Ts> using nth_element_t = typename decltype([]<typename T>(const indexed<I, T>&){return std::type_identity<T>{};}(make_indexer<Ts...>()))::type; template<std::size_t I, is_template<type_list> T> struct nth_type; template<std::size_t I, typename ... Ts> struct nth_type< I, type_list<Ts...> > { typedef nth_element_t<I, Ts...> type; // using type = nth_element_t<I, Ts...>; // Compile error! }; template<std::size_t I, typename T> using nth_type_t = typename nth_type<I, T>::type; int main() { using list = type_list<int, float, double>; using elm1 = nth_type_t<1, list>; using elm2 = nth_type_t<2, list>; return 0; }
核心原因
typedef和using别名在模板特化中的实例化时机存在差异:
typedef是类型声明语句,它的右侧类型表达式会被延迟实例化,直到nth_type<I, type_list<Ts...>>::type被实际使用时才会求值。此时所有模板参数都已完全确定,编译器可以成功完成重载解析。using别名是别名声明,它的右侧类型表达式会在模板特化的定义阶段就被纳入“即时上下文”检查。此时nth_element_t<I, Ts...>需要解析lambda对indexer对象的匹配,但indexer继承了多个indexed<Is, Ts>基类,依赖模板参数的重载解析无法在特化定义阶段完成,导致编译器判定nth_element_t<I, Ts...>无效,最终nth_type特化中无法生成有效的type成员。
解决方案
方案1:将nth_element_t改为类模板(利用延迟实例化)
把nth_element_t从别名模板改为类模板,让它的type成员继承类模板的延迟实例化特性,和typedef行为对齐:
// 替换原nth_element_t的定义 template <std::size_t I, typename ...Ts> struct nth_element { using type = typename decltype([]<typename T>(const indexed<I, T>&){return std::type_identity<T>{};}(make_indexer<Ts...>()))::type; }; template <std::size_t I, typename ...Ts> using nth_element_t = typename nth_element<I, Ts...>::type;
之后就可以正常使用using type = nth_element_t<I, Ts...>;。
方案2:改用更简洁的实现方式(规避重载解析问题)
放弃基于继承的重载匹配思路,改用std::tuple_element实现nth_element_t,这种方式无需依赖复杂的重载解析,using别名可以正常解析:
// 替换原nth_element_t的定义 template <std::size_t I, typename ...Ts> using nth_element_t = typename std::tuple_element_t<I, std::tuple<Ts...>>;
总结
typedef和using别名并非在所有场景下完全等价,using别名的表达式检查时机更早。当右侧表达式依赖模板参数且涉及复杂重载解析时,typedef的延迟实例化可以避免错误,而using别名需要调整实现逻辑来适配即时检查规则。
内容的提问来源于stack exchange,提问作者Евгений Лисицын
相关产品推荐
相关产品推荐

