开发者

Templating (or somehow autoing) the return value of methods

开发者 https://www.devze.com 2023-04-12 22:07 出处:网络
EDIT: To be clear—right off the bat—this is a question about the linguistic abilities of a modern C++ compiler. Not a question about a specific goal. It\'s hard to describe such an开发者_StackOverfl

EDIT: To be clear—right off the bat—this is a question about the linguistic abilities of a modern C++ compiler. Not a question about a specific goal. It's hard to describe such an开发者_StackOverflow社区 abstract concept without clarifying this first and I've realized that some of the confusion revolves around what is commonly done rather than what can possibly be done. This is a very abstract question. Nothing here will compile and this is on purpose. Likewise, I'm not asking how to make this specific case work, but I'm asking if there's a way to get C++ to recognize what I would like to do (via templating or some kind of auto->decltype trick most likely if even possible).

I'm not exactly new to C++, but certainly not an expert. This is a fundamental problem that I've been struggling with since I've rediscovered the power of the language. The end goal here is to elegantly (and with as little code as possible) forward proper polymorphic return values based on calling context. For example...

class A {
    public:
        A& foo() {
            // do something mutant fooish
            return *this;
        };
};

class B: public A {
    public:
        B& bar() {
            // do something mutant barish
            return *this;
        };
};

int main(int argc, char** argv) {
    B yarp;
    yarp.foo().bar();
};

Compile error. Makes sense, C++ is designed to assume that you know nothing about what you're doing (which makes it highly optimizable but sometimes a pain... a high-level-mid-level OOP language).

Obvioiusly C++ compilers have gotten to the point where they're not only aware of what you are asking for (the A().foo() works and B().foo() works scenario), but also in what context your asking for it in (hence auto yarp = B() in C++11, the compiler knows that yarp is an instance of B). Is there a way to leverage this elegantly without having to reproduce a bunch of "using" statements or wrapped methods (which strangely don't get optimized out according to disassemble of gcc binaries)?

So is there a trick here? Something I simply haven't learned online. An auto -> decltype trick or a templating trick? Example:

class A {
    public:
        template <typename R>
        R& foo() {
            // do something fooish
            return (R&)*this;
        };
};

class B: public A {
    public:
        using A::foo<A>; // << even this would be better than nothing (but no where near  optimum)
        B& bar() {
            // do something barish
            return *this;
        };
};

Something even simpler? If you expand this concept to operators of a proxy template class meant for reference counting and gc deallocation, it becomes clear how problematic this becomes. Thanks in advance for any help (oh, and first post on stackoverflow, so if I got any formatting wrong or you have suggestions for a better structured post, apologies around and please point them out).


The obvious solution would be to just seperate it out into two lines:

yarp.foo();
yarp.bar();

or, alternatively, use static_cast's to get back a reference to B&, so

static_cast<B&>(yarp.foo()).bar();

Agreed, that's a little bit more verbose but chaining multiple member-function calls in a heirarchy in one line together like this is pretty unusual syntax for C++. It just doesn't come up a whole lot, so the language doesn't support that idiom terribly well. I have never come across a situation where I ran into this issue yet.

If you want to design some chainable functionality, there are other, better idioms you can use. One example is Boost's Range Adaptors that overload operator| to achieve chaining.

EDIT: Another option is to overload foo() in B&:

class B: public A {
    public:
        B& foo() { A::foo(); return *this; }

        B& bar() {
            // do something mutant barish
            return *this;
        };
};


I don't think there is a auto type detection since compiler even doesn't know what classes will inherit A.

And in your second trial, C++ forbid using a template specialization. So that won't compile.

I think there is another trick you could try is to make A a template

template <typename FinalType>
class A {
    public:
        FinalType& foo() {
            // do something fooish
            return static_cast<FinalType&>(*this);
        };
};

class B: public A<B> {
    public:
        B& bar() {
            // do something barish
            return *this;
        };
};


Erm you declare a instance of class B which has no method foo - so no wonder there is a compile error - did you mean yarp.bar().foo();

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号