Reputation: 71
I want to use "static polymorphism" via CRTP to do something like the following:
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.; }
};
This works, but I'd also like that for the case of the object being instantiated as Derived1
, get_value
returns a const reference to the value instead of returning a copy. So in a way, a kind of "perfect forwarding" for return values.
I tried to declare get_value
's return type like this:
template <typename T>
struct Base
{
decltype(std::declval<T>()._get_value()) get_value() const { return ((T*)this)->_get_value(); }
...
but unsurprisingly GCC complained that this is an invalid use of incomplete type 'struct Derived1'
.
Is there any way to go around this?
Thank you in advance! :)
Upvotes: 2
Views: 517
Reputation: 39101
The reason why GCC is rejecting the proposed solution in the OP is that Base<Derived1>
is being instantiated before Derived1
. This instantiation consists of an instantiation of the signatures of all member functions, but it doesn't instantiate the function bodies themselves.
So we need to defer determination of the signature/return type of the member function until after Derived1
is visible.
One way to do this is by deferring determination of the return type via decltype(auto)
. This makes the return type dependent on the function body (which is not immediately instantiated). It's a C++14 feature, unfortunately.
template <typename T>
struct Base
{
decltype(auto) get_value() const { return ((T*)this)->_get_value(); }
protected:
~Base() {}
};
See https://godbolt.org/z/r1T56n
This is probably covered by dcl.spec.auto and temp.inst.
Alternatively, even in C++11, you can postpone determination of the return type by turning the function into a function template, dependent on some dummy parameter if necessary:
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() {}
};
Upvotes: 4