如何使用SFINAE让编译器自动忽略不存在的模板类特化的成员函数定义
解决方案
方案1:主模板添加默认删除声明(最符合需求)
该方案完全不需要修改你现有特化的代码、不需要修改Sound方法签名、也不需要将方法改为模板,C++11及以上版本都支持:
- 给
Animal主模板添加一个默认的= delete的Sound声明即可,主模板的声明不会对已有的全特化产生任何影响:
enum class Pets { Dog, Cat, Bird }; template <Pets variant> class Animal { // 新增主模板默认声明,未特化的枚举值会走这个分支 void Sound() = delete; }; // 原有特化代码完全不需要修改 template <> class Animal<Pets::Cat> { void Sound(); }; template <> class Animal<Pets::Dog> { void Sound(); }; // 就算你注释掉Bird的特化声明,后续的Sound定义也不会编译报错 // template <> // class Animal<Pets::Bird> { // void Sound(); // }; // 三个方法定义都可以正常编译通过 void Animal<Pets::Cat>::Sound() { printf("Meow!\n"); } void Animal<Pets::Dog>::Sound() { printf("Woof!\n"); } void Animal<Pets::Bird>::Sound() { printf("Peep!\n"); }
- 效果:未特化的枚举值对应的
Sound方法只有在被显式调用时才会触发“调用已删除函数”的编译错误,只要你代码中没有用到该特化的Sound方法,编译器会直接忽略相关定义,不会产生任何报错。
方案2:宏+条件编译生成方法定义
如果你希望完全不生成未特化枚举对应的方法定义,可以用宏包装方法定义逻辑,提前声明你支持的枚举列表:
// 提前定义所有支持的宠物枚举 #define SUPPORTED_PETS(V) \ V(Pets::Cat, "Meow!\n") \ V(Pets::Dog, "Woof!\n") \ V(Pets::Bird, "Peep!\n") // 生成方法定义的宏 #define GENERATE_SOUND_DEF(variant, sound) \ void Animal<variant>::Sound() { printf(sound); } // 批量生成所有支持的方法定义 SUPPORTED_PETS(GENERATE_SOUND_DEF)
该方案的优点是完全不会生成未支持枚举的方法定义,缺点是需要同步维护支持的枚举列表,修改枚举时要同步更新宏定义。
内容的提问来源于stack exchange,提问作者Casper B. Hansen
相关产品推荐
相关产品推荐

