C++带auto的模板函数重载歧义:如何让编译器无歧义选择
解决方案
你的核心问题是重载决议时,warn(FirstAlert auto, LastAlert auto)和warn(Alert auto, LastAlert auto)产生匹配冲突,导致编译器无法优先选择你期望的第一个重载。可以通过以下两种方式解决:
1. 明确概念的层级关系(强化偏序优先级)
调整FirstAlert和LastAlert的概念定义,明确它们是Alert的子概念,让编译器能通过约束的严格性判断出更合适的重载:
#include <iostream> #include <concepts> struct Sound { int amplitude; }; struct Light { int brightness; }; template <typename T> concept Alert = std::derived_from<T, Sound> && std::derived_from<T, Light>; struct FirstSound : Sound{}; struct FirstLight : Light{}; struct FirstSoundLight : FirstSound, FirstLight{}; struct LastSound : Sound{}; struct LastLight : Light{}; struct LastSoundLight : LastSound, LastLight{}; // 明确FirstAlert是Alert的子概念,约束更严格 template <typename T> concept FirstAlert = Alert<T> && std::derived_from<T, FirstSound> && std::derived_from<T, FirstLight>; // 明确LastAlert是Alert的子概念 template <typename T> concept LastAlert = Alert<T> && std::derived_from<T, LastSound> && std::derived_from<T, LastLight>; void warn(FirstAlert auto const& a, LastAlert auto const& b) { std::cout << "Special warning"; } void warn(Alert auto const& a, LastAlert auto const& b) = delete; void warn(Alert auto const& a, Alert auto const& b) { std::cout << "General warning"; } int main() { warn(FirstSoundLight{}, LastSoundLight{}); // 优先匹配第一个重载 return 0; }
C++20的重载决议会优先选择约束更严格的模板,这里FirstAlert是Alert的子约束,所以第一个重载会被优先选中,不会触发delete的重载。
2. 缩小delete重载的匹配范围
如果你需要保留delete重载来禁止特定组合,可以给它添加额外约束,排除第一个参数是FirstAlert的情况,避免和目标重载冲突:
#include <iostream> #include <concepts> struct Sound { int amplitude; }; struct Light { int brightness; }; template <typename T> concept Alert = std::derived_from<T, Sound> && std::derived_from<T, Light>; struct FirstSound : Sound{}; struct FirstLight : Light{}; struct FirstSoundLight : FirstSound, FirstLight{}; struct LastSound : Sound{}; struct LastLight : Light{}; struct LastSoundLight : LastSound, LastLight{}; template <typename T> concept FirstAlert = std::derived_from<T, FirstSound> && std::derived_from<T, FirstLight>; template <typename T> concept LastAlert = std::derived_from<T, LastSound> && std::derived_from<T, LastLight>; void warn(FirstAlert auto const& a, LastAlert auto const& b) { std::cout << "Special warning"; } // 仅当第一个参数是Alert但不是FirstAlert时,才匹配这个delete重载 template <Alert T> requires (!FirstAlert<T>) void warn(T const& a, LastAlert auto const& b) = delete; void warn(Alert auto const& a, Alert auto const& b) { std::cout << "General warning"; } int main() { warn(FirstSoundLight{}, LastSoundLight{}); // 仅匹配第一个重载 return 0; }
这样修改后,当传入FirstSoundLight作为第一个参数时,delete重载会因为!FirstAlert<T>的约束不满足而被排除,编译器只会选择第一个目标重载。
内容的提问来源于stack exchange,提问作者ahfakt
相关产品推荐
相关产品推荐

