使用 C++ 模板时表达约束
Express constraints when using C++ templates
我有一个包装器 class。任何 T 或从 T 派生的对象都应可转换为此 Wrapper。
我还希望 'Someclass' 的任何对象或从 'SomeClass' 派生的对象都可以转换为 Wrapper。两种情况下的实现都需要分开。我怎样才能做到这一点?
以下是我想要的行为:
class SomeClass;
template <typename T>
class Wrapper
{
public:
Wrapper(const T & val)
{
}
template <typename E>
Wrapper(const E & val)
{
static_assert(std::is_base_of<T, E>::value,"Wrapped object needs to be of type T or a type derived from T");
// Some implementation
}
// Is it possible to combine the above 2 constructors into a single
// one? That would help too...
// Can't use SomeClass directly as type because in case of derived
// type of SomeClass, I want the exact type for downstream processing
// into another template call
template <typename E> // ??
Wrapper(const E & val)
{
static_assert(std::is_base_of<SomeClass, E>::value,"Wrapped object needs to be of type SomeClass or a type derived from SomeClass");
// another implementation
}
};
不确定我是否能够正确表达我的问题。非常感谢任何帮助...
让我们从您当前拥有的功能不起作用的原因开始。模板函数(构造函数也是如此)由它们的签名标识。该签名包括模板参数是什么(类型、非类型、模板)、它们的顺序,以及函数参数和 return 类型(构造函数没有 return 类型,但是这与我们要实现的目标无关)。所以你试图做的,涉及两次声明同一个构造函数!还没考虑定义,就重复声明了,当然是不允许的。
那我们能做什么呢?我们可以为每个构造函数添加参数。如果它能区分它们,它们就可以共存。但是共存是不够的,我们希望重载决议能够区别对待它们。我们希望为 类 选择的第一个模板化 c'tor 派生自 T。为 类 选择的第二个模板化 c'tor 派生自 SomeClass
。我们能做到吗?我们可以。如果我们将要添加的模板参数设置为取决于我们的条件,并且在不满足条件时替换失败,则该重载将从考虑中移除。那是 SFINAE!
因此,将所有这些付诸实践:
template <typename E, std::enable_if_t<!std::is_same<SomeClass,T>::value &&
std::is_convertible<E*, T*>::value>* = nullptr>
Wrapper(const E & val)
{
}
template <typename E, std::enable_if_t<std::is_convertible<E*, SomeClass*>::value>* = nullptr>
Wrapper(const E & val)
{
}
那么以上是做什么的呢?它添加了另一个带有默认参数的模板参数。它有条件地这样做,如果在替换期间不满足条件,则 "bad" c'tor 的签名格式错误。并且不会在重载决议中考虑。
我也冒昧地调整了你的条件。您可能只想接受公开派生自 T
和 SomeClass
的 类。 std::is_convertible
表达得更好。 std::is_base_of
也将允许私有和不明确的多重继承。我还确保在 T
是 SomeClass
的情况下,我们不会再次得到两个冲突的构造函数声明。
我有一个包装器 class。任何 T 或从 T 派生的对象都应可转换为此 Wrapper。
我还希望 'Someclass' 的任何对象或从 'SomeClass' 派生的对象都可以转换为 Wrapper。两种情况下的实现都需要分开。我怎样才能做到这一点?
以下是我想要的行为:
class SomeClass;
template <typename T>
class Wrapper
{
public:
Wrapper(const T & val)
{
}
template <typename E>
Wrapper(const E & val)
{
static_assert(std::is_base_of<T, E>::value,"Wrapped object needs to be of type T or a type derived from T");
// Some implementation
}
// Is it possible to combine the above 2 constructors into a single
// one? That would help too...
// Can't use SomeClass directly as type because in case of derived
// type of SomeClass, I want the exact type for downstream processing
// into another template call
template <typename E> // ??
Wrapper(const E & val)
{
static_assert(std::is_base_of<SomeClass, E>::value,"Wrapped object needs to be of type SomeClass or a type derived from SomeClass");
// another implementation
}
};
不确定我是否能够正确表达我的问题。非常感谢任何帮助...
让我们从您当前拥有的功能不起作用的原因开始。模板函数(构造函数也是如此)由它们的签名标识。该签名包括模板参数是什么(类型、非类型、模板)、它们的顺序,以及函数参数和 return 类型(构造函数没有 return 类型,但是这与我们要实现的目标无关)。所以你试图做的,涉及两次声明同一个构造函数!还没考虑定义,就重复声明了,当然是不允许的。
那我们能做什么呢?我们可以为每个构造函数添加参数。如果它能区分它们,它们就可以共存。但是共存是不够的,我们希望重载决议能够区别对待它们。我们希望为 类 选择的第一个模板化 c'tor 派生自 T。为 类 选择的第二个模板化 c'tor 派生自 SomeClass
。我们能做到吗?我们可以。如果我们将要添加的模板参数设置为取决于我们的条件,并且在不满足条件时替换失败,则该重载将从考虑中移除。那是 SFINAE!
因此,将所有这些付诸实践:
template <typename E, std::enable_if_t<!std::is_same<SomeClass,T>::value &&
std::is_convertible<E*, T*>::value>* = nullptr>
Wrapper(const E & val)
{
}
template <typename E, std::enable_if_t<std::is_convertible<E*, SomeClass*>::value>* = nullptr>
Wrapper(const E & val)
{
}
那么以上是做什么的呢?它添加了另一个带有默认参数的模板参数。它有条件地这样做,如果在替换期间不满足条件,则 "bad" c'tor 的签名格式错误。并且不会在重载决议中考虑。
我也冒昧地调整了你的条件。您可能只想接受公开派生自 T
和 SomeClass
的 类。 std::is_convertible
表达得更好。 std::is_base_of
也将允许私有和不明确的多重继承。我还确保在 T
是 SomeClass
的情况下,我们不会再次得到两个冲突的构造函数声明。