假设我有一个模板类:
template <class T>
class Foo
{
public:
explicit Foo(const T& value)
: m_Value(value)
{
}
bool Bar(const T& value)
{
return m_Value == value;
}
private:
T m_Value;
};
让我说我有一些其他用户类型,例如:
class A
{
};
然后这段代码完全有效,即使类 A 没有定义相等运算符:
int main(int argc, char* argv[])
{
A a;
Foo<A> foo(a);
return 0;
}
但如果我使 Foo :: Bar()虚拟:
virtual bool Bar(const T& value)
{
return m_Value == value;
}
代码不再编译:
错误C2676:二进制'==':'A'未定义此运算符或转换为预定义运算符可接受的类型
我完全理解为什么这是一个问题。如果我错了,请纠正我,但我的理解是因为函数是虚函数,编译器必须编译函数(即使它从未调用过),以便它可以在 Foo <的v表中引用它。 /强>
我想知道是否有办法绕过这个问题。我想要一个模板类来处理可能只实现部分接口的泛型类型。只要没有使用丢失的位,代码应该编译好。这与许多STD容器的工作方式类似,但它们不使用虚函数。
我该怎么做?对此有优雅的解决方案吗?
答案 0 :(得分:1)
正如上面的Kerrek SB所解释的,虚拟函数总是在模板实例化时实现。因此,当没有使用虚拟方法时,没有办法让你的程序编译得很好和如果它被使用并且你想要包装的类不提供它自己的{ {1}}。
但是,您可以在运行时使程序崩溃(使用operator==
/ assert
)或抛出异常。
免责声明:我不认为做你想做的事是个好主意,因为它可以创建不支持他们声称的界面的课程提供。使用以下风险需要您自担风险。
这里的方法是为你想要提供的每个方法使用自定义类型特征,即使包装类本身没有实现它。在您的情况下,这只是terminate
,相应的代码如下所示:
operator==
如果您无法访问c ++ 1z但您可以创建自己的namespace traits {
template <typename T>
using operator_eq_t = decltype(std::declval<T>() == std::declval<T>());
template <typename, typename = void>
struct has_operator_eq : std::false_type {};
// check that operator== is defined and returns the correct type `bool`.
template <typename T>
struct has_operator_eq<T, std::void_t<operator_eq_t<T>>>
: std::is_same<operator_eq_t<T>, bool> {};
} // namespace traits
版本,其他所有内容都是有效的C ++ 14:
std::void_t
有了这个,您可以使用标签分发创建包装类模板:
template <typename...>
using void_t = void
可以找到一个工作示例here。