Шаблонирование (или как-то автоматическое) возвращаемое значение методов - PullRequest
2 голосов
/ 12 октября 2011

РЕДАКТИРОВАТЬ: Чтобы быть ясно - сразу же - это вопрос о лингвистических способностях современного компилятора C ++. Не вопрос о конкретной цели. Трудно описать такую ​​абстрактную концепцию, не пояснив ее сначала, и я понял, что некоторая путаница связана с тем, что обычно делается, а не с тем, что возможно сделать. Это очень абстрактный вопрос. Ничто здесь не будет компилироваться, и это специально. Аналогично, я не спрашиваю, как заставить этот конкретный случай работать, но я спрашиваю, есть ли способ заставить C ++ распознавать, что я хотел бы сделать (с помощью шаблонов или какого-то трюка auto-> decltype, наиболее вероятно, если даже возможно).

Я не совсем новичок в C ++, но, конечно, не эксперт. Это фундаментальная проблема, с которой я боролся с тех пор, как заново открыл силу языка. Конечной целью здесь является элегантное (и с как можно меньшим количеством кода) пересылка правильных полиморфных возвращаемых значений на основе вызывающего контекста. Например ...

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();
};

Ошибка компиляции. Имеет смысл, C ++ разработан так, чтобы предполагать, что вы ничего не знаете о том, что вы делаете (что делает его чрезвычайно оптимизируемым, но иногда это боль ... язык ООП высокого уровня среднего уровня).

Компиляторы C ++ Obvioiusly дошли до того, что они знают не только о том, что вы запрашиваете (работает A (). Foo () и сценарий B (). Foo ()), но и что в контексте вашего запроса (следовательно, auto yarp = B () в C ++ 11, компилятор знает, что yarp является экземпляром B). Есть ли способ использовать это элегантно без необходимости воспроизводить кучу «использующих» операторов или упакованных методов (которые странным образом не оптимизируются в соответствии с дизассемблированием двоичных файлов gcc)?

Так тут есть хитрость? Что-то, чего я просто не узнал в Интернете. Трюк auto -> decltype или трюк с шаблонами? Пример:

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;
        };
};

Что-то еще проще? Если вы расширите эту концепцию до операторов прокси-шаблона, предназначенного для подсчета ссылок и освобождения gc, станет ясно, насколько проблематичным это становится. Заранее благодарен за любую помощь (о, и первое сообщение о stackoverflow, поэтому, если я ошибся в форматировании или у вас есть предложения по улучшению структурированного сообщения, примите извинения и укажите их).

Ответы [ 3 ]

2 голосов
/ 12 октября 2011

Очевидным решением будет просто разделить его на две строки:

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

или, альтернативно, используйте static_cast для возврата ссылки на B &, поэтому

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

Согласен, это немного более многословно, но объединяет несколько вызовов функций-членов в иерархии в одну строку, как это довольно необычный синтаксис для C ++. Он не очень подходит, поэтому язык не очень хорошо поддерживает эту идиому. Я никогда не сталкивался с ситуацией, когда столкнулся с этой проблемой.

Если вы хотите создать какую-то цепную функциональность, есть и другие идиомы, которые вы можете использовать. Одним из примеров являются Адаптеры диапазона Boost, которые перегружают оператор | добиться цепочки.

РЕДАКТИРОВАТЬ: Другой вариант - перегрузить foo () в B &:

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

        B& bar() {
            // do something mutant barish
            return *this;
        };
};
2 голосов
/ 12 октября 2011

Я не думаю, что есть автоматическое определение типа, так как компилятор даже не знает, какие классы будут наследовать A.

И в вашем втором испытании C ++ запрещает using шаблонную специализацию. Так что это не скомпилируется.

Я думаю, что есть еще один трюк, который вы можете попробовать сделать A шаблон

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;
        };
};
0 голосов
/ 12 октября 2011

Эм, вы объявляете экземпляр класса B, у которого нет метода foo - поэтому неудивительно, что есть ошибка компиляции - вы имели в виду yarp.bar () Foo ();.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...