转发CRTP派生的类方法的返回类型

问题描述

我想通过CRTP使用“静态多态性”来执行以下操作:

template <typename T>
struct Base
{
    double get_value() const { return ((T*)this)->_get_value(); }

protected:
    ~Base() {}
};

struct Derived1 : public Base<Derived1>
{
    double value;

    Derived1() : value(3.) {}

    const double& _get_value() const { return value; }
};

struct Derived2 : public Base<Derived2>
{
    double _get_value() const { return 5.; }
};

这可行,但是对于对象实例化为Derived1的情况,我也希望get_value返回对该值的const引用,而不返回副本。因此,从某种意义上说,这是一种对返回值的“完美转发”。

我试图声明get_value的返回类型,如下所示:

template <typename T>
struct Base
{
    decltype(std::declval<T>()._get_value()) get_value() const { return ((T*)this)->_get_value(); }
    ...

但毫不奇怪,海湾合作委员会抱怨说这是invalid use of incomplete type 'struct Derived1'

有什么办法解决这个问题吗?

提前谢谢! :)

解决方法

GCC拒绝OP中提出的解决方案的原因是Base<Derived1>之前 Derived1实例化。此实例化包括所有成员函数的签名的实例化,但不实例化函数体本身。

因此,我们需要推迟确定成员函数的签名/返回类型,直到之后 Derived1可见。

一种方法是通过decltype(auto)推迟确定返回类型。这使得返回类型取决于函数主体(不会立即实例化)。不幸的是,这是C ++ 14的功能。

template <typename T>
struct Base
{
    decltype(auto) get_value() const { return ((T*)this)->_get_value(); }

protected:
    ~Base() {}
};

请参见https://godbolt.org/z/r1T56n


dcl.spec.autotemp.inst可能涵盖了这一点。


或者,即使在C ++ 11中,也可以通过将函数转换为函数模板来推迟确定返回类型,如果需要,可以依赖于某些伪参数:

template <typename T>
struct Base
{
    template<typename U=T>
    decltype(std::declval<U>()._get_value()) get_value() const {
        return ((T*)this)->_get_value();
    }

protected:
    ~Base() {}
};