【问题标题】:Specifying allowed arguments in templates在模板中指定允许的参数
【发布时间】:2012-08-15 02:15:48
【问题描述】:

我可以准确地指定模板可以接收什么样的参数吗?例如,我想创建一个模板,该模板只能用属于或扩展类A 的类来实例化。在 Java 中,泛型通过以下方式支持这一点:

class B<T extends A> { }

C++ 中的模板可以实现类似的功能吗?

template <typename T (?)> class B { }

【问题讨论】:

  • 您可以通过static_assertstd::is_same(对于A)和std::is_base_of(对于派生)执行此操作。我会写一个例子。我认为std::enable_if 也可能会这样做,但我不能说我用过。
  • 这是example。您可以更改传入的内容(ab 有效,但 c 无效)。如果您没有 C++11(用于静态断言),我觉得 std::enable_if 可能会有所帮助,但我不确定如何使用它。它也不会提供明确的错误消息。
  • @chris,我似乎没有type_traits 头文件。我应该在哪里寻找static_assertstd::is_base_ofstd::enable_if
  • 没有 type_traits?这很奇怪,因为它是标准标题。它应该与所有其他人一起。 static_assert 仅适用于 C++11,实际上 enable_if 似乎也是如此,呵呵。 is_base_of 也是,所以我想这需要 C++11,因为我想不出任何其他流畅的方法来做到这一点。为了使用它,您需要一个支持这些功能和-std=c++11 选项的编译器。
  • @chris,我明白了...所以这是最近添加的?

标签: c++ templates generics sfinae static-assert


【解决方案1】:

有两种方法可以做到这一点。

首先,通过使用std::enable_ifstd::is_base_of&lt;A, T&gt;::value 作为条件的隐藏虚拟模板参数。如果后一个表达式计算为false,则嵌套的type 不存在于std::enable_if 中。如果您在重载函数上使用它,SFINAE 则意味着“替换失败不是错误”,并且有问题的重载将从可行函数集中删除。但是在这种情况下,没有其他类模板可以匹配您的调用,然后您确实会收到编译时错误。

SFINAE 是一种非常微妙的机制,很容易出错。例如。如果您有多个具有不同 SFINAE 条件的类专业化,则必须确保它们都不重叠,否则您会产生歧义。

其次,您可以在类的主体内使用std::is_base_of&lt;A,T&gt;::value 来做一个简单的static_assert。与 SFINAE 方法相比,此方法的优点是您还可以指定更易读的错误消息。一个缺点是您总是会收到错误,并且您无法静默抑制此特定模板并选择另一个模板。但总的来说,我认为在你的情况下推荐这种方法。

#include<type_traits>

class A {};
class C: public A {};
class D {};

// first alternative: SFINAE on hidden template parameter
template
<
    typename T, 
    typename /* dummy */ = typename std::enable_if< 
        std::is_base_of<A, T>::value
    >::type
>
class B
{
};

// second alternative: static_assert inside class
template
<
    typename T
>
class E
{
    static_assert(std::is_base_of<A, T>::value, "A should be a base of T");
};

int main()
{
    B<A> b1;
    B<C> c1;
    //B<D> d1; // uncomment this line to get a compile-time error

    E<A> b2;
    E<C> c2;
    //E<D> d2; // uncomment this line to get a compile-time error

    return 0;
}

正如 cmets 中所指出的,您可以使用像样的 C++11 编译器(VC++ 2010 或更高版本,gcc 4.5 或更高版本)或 Boost 或 TR1 库来获得 &lt;type_traits&gt; 功能。但是请注意,std::is_base_of&lt;A, A&gt;::value 的计算结果为 true,但旧的 boost::is_base_of&lt;A, A&gt;::value 用于计算为 false

【讨论】:

    【解决方案2】:

    您可以使用static_assertis_base_of 做到这一点:

    #include <type_traits>
    template<typename T> class D {
        static_assert(std::is_base_of<A, T>::value, "must be derived from A");
    };
    

    或者你可以使用enable_if:

    #include <type_traits>
    template<typename T, typename = void> class D;
    template<typename T> class D<T, typename std::enable_if<std::is_base_of<A, T>::value>::type> {
    };
    

    对于 C++03,你可以使用 boost; is_base_of 来自Boost.TypeTraitsstatic_assert 来自Boost.StaticAssertenable_if 来自Boost.EnableIf

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-09-15
      • 1970-01-01
      • 2019-10-10
      • 1970-01-01
      • 2021-07-18
      • 2015-02-20
      • 1970-01-01
      • 2014-07-27
      相关资源
      最近更新 更多