你好,模板向导,
我一直在制作一个实用程序模板,还有一个问题需要解决,但似乎无法解决。这篇文章中的代码是对所讨论代码的简化,该代码显示了相同的问题。
假设您有一个模板专业化,如下所示。
enum class Pets { Dog, Cat, Bird };
template <Pets variant>
class Animal;
template <>
class Animal<Pets::Cat> {
void Sound();
};
template <>
class Animal<Pets::Dog> {
void 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"); }
然后我说我可能不会宣布Animal<Pets::Bird>
的专业化,这毁了一切。::Sound()
的后一个定义将无法编译,因为该类型不存在。现在,在我看来,这似乎需要一个SFINAE解决方案,因为我们有一个我们不想存在的类型的不需要的方法实现。
我希望编译器忽略最后一个方法定义,而不是失败编译,而不更改方法签名——也就是说,我不想让方法本身成为模板。
你对如何做到这一点有什么建议吗?
您不能这样做:SFINAE是关于重载解析的,虽然它也支持非常类似的选择部分专业化的情况,但它不是一种通用的条件编译技术。当然,你可以使用预处理器,你可以做一些技巧,比如声明不需要的函数private
,使其"不存在",但你不能定义一个从未被提及的函数。
我确实想出了一个解决方案,它非常巧妙地声明了一个包罗万象的解决方案,因为这些类型将被丢弃——不是我所要求的,而是更接近目标。
这至少可以处理那些在编译中失败的方法的定义。
enum class Pets { Dog, Cat, Bird };
static constexpr Pets kChosenPet = Pets::Cat;
struct AbstractAnimal {
virtual void Sound() = 0;
};
template <Pets variant = kChosenPet, bool B = (variant == kChosenPet)>
struct Animal : public AbstractAnimal {
void Sound();
};
template <Pets variant>
struct Animal<variant, true> : public AbstractAnimal {
void Sound();
};
template <>
void Animal<Pets::Cat>::Sound() {
printf("Meow!n");
}
template <>
void Animal<Pets::Dog>::Sound() {
printf("Woof!n");
}
template <>
void Animal<Pets::Bird>::Sound() {
printf("Peep!n");
}
可能我不明白你的问题。但是,仅仅将Sound()
的声明移动到Animal
有什么问题呢?
#include <stdio.h>
#include <type_traits>
enum class Pets { Dog, Cat, Bird };
template <Pets variant>
class Animal {
void Sound();
};
template<>
void Animal<Pets::Cat>::Sound() { printf("Meow!n"); }
template<>
void Animal<Pets::Dog>::Sound() { printf("Woof!n"); }
template<>
void Animal<Pets::Bird>::Sound() { printf("Peep!n"); }
"SFINAE";是指";替换失败不是错误";。这始于";"补偿";,模板实例化过程的早期部分。当存在失败模板的替代方案时,例如其他函数过载时,SFINAE才不是错误。
现在在void Animal<Pets::Bird>::Sound()
中,没有替换,因此SFINAE无法应用。我们也不能引入它——正常的方式是通过std::enable_if
,它可以将非模板声明转换为可能实例化也可能不实例化的模板声明。但这不起作用——这种专门化不能是模板声明。
这种情况可以简化:
#ifdef FOO
class Foo { int bar; }
#endif
int Foo::bar() { return 1; } // Also not fixable by SFINAE, for the same reason.