提问者:小点点

C++静态多态性(CRTP)和使用来自派生类的typedefs


我在维基百科上读过一篇关于C++中用于实现静态(阅读:编译时)多态性的奇怪的重复出现的模板模式的文章。我想对它进行泛化,以便可以根据派生类型更改函数的返回类型。(这似乎是可能的,因为基类型从模板参数知道派生类型)。不幸的是,下面的代码不会使用MSVC2010进行编译(我现在不容易访问gcc,所以我还没有尝试过它)。有人知道为什么吗?

template <typename derived_t>
class base {
public:
    typedef typename derived_t::value_type value_type;
    value_type foo() {
        return static_cast<derived_t*>(this)->foo();
    }
};

template <typename T>
class derived : public base<derived<T> > {
public:
    typedef T value_type;
    value_type foo() {
        return T(); //return some T object (assumes T is default constructable)
    }
};

int main() {
    derived<int> a;
}

顺便说一下,我有一个使用额外模板参数的解决方案,但我不喜欢它--当向继承链中传递许多类型时,它会变得非常冗长。

template <typename derived_t, typename value_type>
class base { ... };

template <typename T>
class derived : public base<derived<T>,T> { ... };

编辑:

MSVC 2010在这种情况下给出的错误消息是错误C2039:“value_type”:不是“derived<;t>;”的成员

G++4.1.2(通过codepad.org)指出错误:“class derived<;int>;”中没有名为“value_type”的类型


共3个答案

匿名用户

当您将派生的用作基类列表中的模板参数时,它是不完整的。

一个常见的解决方法是使用traits类模板。这是你的例子,叛徒。这说明了如何通过Traits使用派生类中的类型和函数。

// Declare a base_traits traits class template:
template <typename derived_t> 
struct base_traits;

// Define the base class that uses the traits:
template <typename derived_t> 
struct base { 
    typedef typename base_traits<derived_t>::value_type value_type;
    value_type base_foo() {
        return base_traits<derived_t>::call_foo(static_cast<derived_t*>(this));
    }
};

// Define the derived class; it can use the traits too:
template <typename T>
struct derived : base<derived<T> > { 
    typedef typename base_traits<derived>::value_type value_type;

    value_type derived_foo() { 
        return value_type(); 
    }
};

// Declare and define a base_traits specialization for derived:
template <typename T> 
struct base_traits<derived<T> > {
    typedef T value_type;

    static value_type call_foo(derived<T>* x) { 
        return x->derived_foo(); 
    }
};

您只需对用于base的模板参数derived_t的任何类型专门化base_traits,并确保每个专门化都提供了base所需的所有成员。

匿名用户

使用traits的一个小缺点是,您必须为每个派生类声明一个traits。您可以编写一个不那么冗长和简单的变通方法,如下所示:

template <template <typename> class Derived, typename T>
class base {
public:
    typedef T value_type;
    value_type foo() {
        return static_cast<Derived<T>*>(this)->foo();
    }
};

template <typename T>
class Derived : public base<Derived, T> {
public:
    typedef T value_type;
    value_type foo() {
        return T(); //return some T object (assumes T is default constructable)
    }
};

int main() {
    Derived<int> a;
}

匿名用户

在C++14中,您可以删除typedef并使用函数autoreturn type deliveration:

template <typename derived_t>
class base {
public:
    auto foo() {
        return static_cast<derived_t*>(this)->foo();
    }
};

这是因为base::foo返回类型的推演会延迟到derived_t完成。

相关问题


MySQL Query : SELECT * FROM v9_ask_question WHERE 1=1 AND question regexp '(c++|静态|多态性|crtp|派生类|typedefs)' ORDER BY qid DESC LIMIT 20
MySQL Error : Got error 'repetition-operator operand invalid' from regexp
MySQL Errno : 1139
Message : Got error 'repetition-operator operand invalid' from regexp
Need Help?