将C ++模板参数限制为子类


79

如何强制将模板参数T作为特定类的子类Baseclass?像这样:

template <class T : Baseclass> void function(){
    T *object = new T();

}

3
您试图通过此操作完成什么?
某物

2
我只想确保T实际上是子类的实例或类本身。我提供的功能内部的代码几乎无关紧要。
phant0m 2010年

6
相反,这非常相关。它确定将工作投入该测试是否是一个好主意。在许多(所有?)情况下,绝对没有必要自己强制执行此类约束,而是让编译器在实例化时执行此约束。例如,对于可接受的答案,最好检查是否T源自Baseclass。到目前为止,该检查是隐式的,对于重载解决方案不可见。但是,如果在任何地方都没有这样的隐式约束,则似乎没有理由进行人为约束。
Johannes Schaub-litb

1
是的我同意。但是,我只是想知道是否有一种方法可以完成此操作:)但是,当然,您有一个很有效的观点,感谢您的见解。
phant0m

Answers:


50

在这种情况下,您可以执行以下操作:

template <class T> void function(){
    Baseclass *object = new T();

}

如果T不是Baseclass的子类(或TBaseclass),则不会编译。


啊,是的,这是个好主意。谢谢!我接受了,然后在模板定义中无法定义它吗?
phant0m 2010年

2
@ phant0m:正确。您不能显式约束模板参数(使用概念除外,这些概念在c ++ 0x中被考虑,但随后被删除)。所有约束都由您对其执行的操作隐式发生(换句话说,唯一的约束是“类型必须支持对其执行的所有操作”)。
sepp2k 2010年

1
嗯ic 非常感谢您的澄清!
phant0m 2010年

8
这将执行T()构造函数,并要求存在T()构造函数。请参阅我的答案,以寻求避免那些要求的方法。
Douglas Leeder

3
很清晰,但是如果T是“重”类,这是一个问题。
3Dave

84

使用符合C ++ 11的编译器,您可以执行以下操作:

template<class Derived> class MyClass {

    MyClass() {
        // Compile-time sanity check
        static_assert(std::is_base_of<BaseClass, Derived>::value, "Derived not derived from BaseClass");

        // Do other construction related stuff...
        ...
   }
}

我已经在CYGWIN环境中使用gcc 4.8.1编译器对此进行了测试-因此它也应该在* nix环境中也可以工作。


对我来说,它的工作方式也如下:template<class TEntity> class BaseBiz { static_assert(std::is_base_of<BaseEntity, TEntity>::value, "TEntity not derived from BaseEntity");...
马提亚斯·迪特·沃勒

1
我认为这是最易读的答案,可以避免在运行时添加额外的代码。
凯尔(Kyle),

50

要在运行时执行较少的无用代码,请查看:http : //www.stroustrup.com/bs_faq2.html#constraints ,其中提供了一些类,这些类可以有效地执行编译时间测试,并产生更好的错误消息。

尤其是:

template<class T, class B> struct Derived_from {
        static void constraints(T* p) { B* pb = p; }
        Derived_from() { void(*p)(T*) = constraints; }
};

template<class T> void function() {
    Derived_from<T,Baseclass>();
}

2
对我来说,这是最好,最有趣的答案。请务必查看Stroustrup的FAQ,以了解有关您可以与此类似地强制实施的各种约束的更多信息。
Jean-Philippe Pellet

1
的确,这是一个答案!谢谢。提到的网站移到了这里:stroustrup.com/bs_faq2.html#constraints
Jan Korous

这是一个很好的答案。有没有什么好的办法,以避免警告unused variable 'p'unused variable 'pb'
Filip S.

@FilipS。在(void)pb;之后添加B* pb = p;
bit2shift

11

您不需要概念,但是可以使用SFINAE:

template <typename T>
boost::enable_if< boost::is_base_of<Base,T>::value >::type function() {
   // This function will only be considered by the compiler if
   // T actualy derived from Base
}

请注意,这只会在满足条件时实例化该功能,但如果不满足条件,则不会提供明显的错误。


如果像这样包装所有功能怎么办?顺便说一句,它返回什么?
the_drow

所述enable_if采用的第二类型参数,默认值为void。表达式enable_if< true, int >::type表示类型int。我真的不明白您的第一个问题是什么,您可以使用SFINAE进行任何操作,但是我不太了解您打算在所有功能上使用此功能。
大卫·罗德里格斯(DavidRodríguez)-dribeas'7

7

从C ++ 11开始,您不需要Boost或static_assert。C ++ 11引入is_base_ofenable_if。C ++ 14引入了便利类型enable_if_t,但是如果您对C ++ 11感到困惑,则可以简单地使用它enable_if::type

选择1

大卫·罗德里格斯(DavidRodríguez)的解决方案可以重写如下:

#include <type_traits>

using namespace std;

template <typename T>
enable_if_t<is_base_of<Base, T>::value, void> function() {
   // This function will only be considered by the compiler if
   // T actualy derived from Base
}

选择2

从C ++ 17开始,我们有了is_base_of_v。该解决方案可以进一步重写为:

#include <type_traits>

using namespace std;

template <typename T>
enable_if_t<is_base_of_v<Base, T>, void> function() {
   // This function will only be considered by the compiler if
   // T actualy derived from Base
}

选择3

您也可以只限制整个模板。您可以使用此方法定义整个类。请注意如何enable_if_t删除的第二个参数(之前已将其设置为void)。它的默认值实际上是void,但这并不重要,因为我们没有使用它。

#include <type_traits>

using namespace std;

template <typename T,
          typename = enable_if_t<is_base_of_v<Base, T>>>
void function() {
   // This function will only be considered by the compiler if
   // T actualy derived from Base
}

从模板参数的文档中,我们看到这typename = enable_if_t...是一个带有空名称的模板参数。我们只是使用它来确保类型定义存在。特别enable_if_t是如果Base不是的基数,则不会定义T

上面的技术在中作为示例给出enable_if


如果可以编写如下的替代方案3,这会很好吗? template <class T : Base>
Macsinus

4

您可以使用Boost Concept CheckBOOST_CONCEPT_REQUIRES

#include <boost/concept_check.hpp>
#include <boost/concept/requires.hpp>

template <class T>
BOOST_CONCEPT_REQUIRES(
    ((boost::Convertible<T, BaseClass>)),
(void)) function()
{
    //...
}

0

通过调用模板中存在于基类中的函数。

如果您尝试使用无法访问此函数的类型实例化模板,则会收到编译时错误。


3
这不能确保T 是a, BaseClass因为在中的声明成员BaseClass可以在的声明中重复T
Daniel Trebbien
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.