正如其他人建议的那样,您可以使用enable_if 和SFINAE (Substition Failure Is Not An Error),但这可能会出现问题。它在编译器中可能很脆弱,如果处理不当可能会导致 ODR 出现问题,可能会导致与从其他范围 (ADL) 中提取的函数发生冲突,并且可能不是最佳选择。
您确实有几个替代选项,它们不那么变化无常,并且可能使您的代码更易于理解和维护。一种选择(如 cmets 中所述)是使用 static_assert 和 is_base_of。这是迄今为止(恕我直言)您想要完成的最简单的解决方案。如果类型不是基类 Mom 或派生自它,这种方法允许您维护函数的单个实现并强制编译时失败。
#include <type_traits>
struct Mom { };
struct Child : Mom { };
struct Uncle { };
template <typename T>
void AcceptOnlyMomAndChildren(T)
{
static_assert(std::is_base_of<Mom, T>::value, "Not derived from Base.");
}
int main()
{
Child child;
Uncle uncle;
AcceptOnlyMomAndChildren(child);
AcceptOnlyMomAndChildren(uncle); // Fails
}
如果您需要提供多个函数来处理不是从特定基类派生的类型,使用标签调度可能更有意义。这在编译器中的变化要小得多,而且必须更容易理解。如果您需要为派生自 mom 的类型提供特殊处理(在您的示例中),它也可以轻松扩展。
#include <type_traits>
#include <iostream>
struct Mom { };
struct Child : Mom { };
struct Uncle { };
template <typename T>
void Func(T&, std::true_type)
{
std::cout << "Derived from Base." << std::endl;
}
template <typename T>
void Func(T&, std::false_type)
{
std::cout << "Not derived from Base." << std::endl;
}
template <typename T>
void Func(T& arg)
{
Func(
arg,
typename std::conditional<
std::is_base_of<Mom, T>::value,
std::true_type, std::false_type>::type());
}
int main()
{
Child child;
Uncle uncle;
Func(child);
Func(uncle);
}
在上面的示例中,std::conditional 用于指定附加参数的类型,以确定调用 Func 的哪个实现。如有必要,您可以将其替换为您自己的类型特征方案以支持其他实现。