Есть ли разумное использование функции, возвращающей анонимную структуру?

Вот (искусственный) пример использования функции, которая возвращает анонимную структуру и делает «что-то» полезное:

#include <iostream>

template<typename T>
T* func(T* t, float a, float b) {
    if(!t) {
        t = new T;
        t->a = a;
        t->b = b;
    } else {
        t->a += a;
        t->b += b;
    }
    return t;
}

struct {
    float a, b;
}* foo(float a, float b) {
    if(a==0) return 0;
    return func(foo(a-1,b), a, b);
}

int main() {
    std::cout << foo(5,6)->a << std::endl;
    std::cout << foo(5,6)->b << std::endl;

    void* v = (void*)(foo(5,6));
    //[1] delete f now because I know struct is floats only.
    float* f = (float*)(v);

    std::cout << f[0] << std::endl;
    std::cout << f[1] << std::endl;

    delete[] f;

    return 0;
}

Есть несколько моментов, которые я хотел бы обсудить:

  1. Как видно, этот код имеет утечку, в любом случае я не могу утечь, не зная, что такое базовое определение структуры? см. комментарий [1].
  2. Мне нужно вернуть указатель на анонимную структуру, чтобы я мог создать экземпляр объекта в шаблонной функции func, могу ли я сделать что-то подобное, не возвращая указатель?
  3. Я думаю, самое главное, есть ли вообще ЛЮБОЕ (реальное) применение для этого? Поскольку пример, приведенный выше, протекает и, по общему признанию, надуманный.

Кстати, функция foo(a,b) возвращает структуру, содержащую два числа, сумму всех чисел от 1 до а и произведение а и b.

Возможно, строка new T могла бы как-то использовать boost::shared_ptr, чтобы избежать утечек, но я этого не пробовал. Будет ли это работать?

Я думаю, что просто пытался удалить анонимную структуру как массив с плавающей запятой, что-то вроде float* f = new float[2]. Что может быть неправильным, как предполагает комментарий ниже, так что же можно сделать? можно ли вообще удалить?

Я могу скомпилировать и запустить этот код «как есть» на VS2008, возможно, некоторые нестандартные расширения могут использоваться VS, но он работает и дает 15 и 30 в качестве ответа.

Судя по ответам, я считаю, что это приспособление является специфичным для VS2008, оно не соответствует стандартам и, следовательно, не переносимо. Жаль, однако, мне бы хотелось увидеть, какое колдовство придумали люди Stackoverflow или Boost, если бы это было в их арсенале :). Спасибо всем.


person Akanksh    schedule 08.04.2010    source источник
comment
delete[] в памяти, выделенной с помощью new? ОЙ!   -  person vladr    schedule 08.04.2010
comment
Я знаю, я просто пытаюсь удалить анонимную структуру как массив с плавающей запятой, что-то вроде float* f = new float[2].   -  person Akanksh    schedule 08.04.2010
comment
Я почти уверен, что вы вообще не можете этого сделать, поэтому не имеет значения, есть ли применение или нет.   -  person Dennis Zickefoose    schedule 08.04.2010
comment
@Dennis Zickefoose: я могу скомпилировать и запустить это на VS2008, возможно, могут использоваться некоторые нестандартные расширения. Но я уверен, что это работает на VS2008.   -  person Akanksh    schedule 08.04.2010
comment
Попробуйте сделать что-нибудь после удаления []; там может случиться всякое.   -  person Gorpik    schedule 08.04.2010


Ответы (5)


На данный момент ваш код не является переносимым; например, он не будет строиться с gcc.

Раздел 14.3.1/2 стандарта гласит:

Локальный тип, тип без связи, безымянный тип или тип
, составленный из любого из этих типов, не должны использоваться в качестве шаблона-
аргумента
для параметра типа шаблона.

См. пункт 488 в отчетах о дефектах стандартного базового языка C++, Редакция 69 и документ N2657 для одной возможной эволюции.

ОБНОВЛЕНИЕ 1

Предполагая, что ваш код был правильно сформирован, тогда:

  1. вы можете переписать:

    std::cout << foo(5,6)->a << std::endl;
    

    as

    std::cout << std::auto_ptr(foo(5,6))->a << std::endl;
    
  2. вы можете вернуть анонимное struct по значению при условии, что анонимная структура имеет конструктор, принимающий другой тип (анонимный или нет, который вы сможете инициализировать внутри тела вашего метода) - за исключением, конечно, как вы указать конструктор для анонимной структуры? :)

  3. никакого реального использования, которое я вижу, кроме чрезвычайно запутанного способа попытки не присваивать явное имя структуре; обычно используются анонимные структуры (технически незаконные в C++, но поддерживаемые различными компиляторами в качестве расширений), чтобы не загрязнять пространство имен, как правило, путем немедленного создания экземпляра (вы можете, например, увидеть, как одноразовые функторы создаются и передаются вниз как анонимные структуры - опять же, технически недопустимый C++.)

ОБНОВЛЕНИЕ 2

Спасибо gf за ссылку на соответствующую часть стандарта C++, касающуюся новых типов, которые не могут быть определены в возвращаемом типе.

ОБНОВЛЕНИЕ 3

Приведение этого сюда из комментариев: вызов delete[] в памяти, выделенной с помощью new (в отличие от new[]), является приглашением к повреждению кучи. Вызов delete для указателя, тип которого вам неизвестен, технически не определен (какой деструктор должен быть вызван?), но в случае POD (ваша анонимная структура является одной из них) вы можете избежать неприятностей с этим в этом ужасном хакерский способ:

 delete (int*)f;

Конечно, если бы ваш код был волшебным образом правильно сформирован, std::auto_ptr смог бы сохранить анонимный тип и позаботился бы о правильном и изящном вызове delete за вас.

person vladr    schedule 08.04.2010
comment
Спасибо за возможную эволюцию в этом контексте. - person Akanksh; 08.04.2010
comment
@Vlad: Даже с интегрированным N2657 его непереносимость из-за того, что определения типов не разрешены в возвращаемых типах. - person Georg Fritzsche; 08.04.2010
comment
@gf, правда, когда я говорю assuming your code were well-formed, это большое предположение, выходящее за рамки N2657, но давайте ради аргумента притворимся, что g++ также поддерживает это как расширение в будущем (gcc прекрасно проглотит его при сборке C, так же, как MSVC сделал для ОП...) Кстати, спасибо за стандартную ссылку! :) - person vladr; 08.04.2010
comment
Писал это до обновления. Даже тогда, как говорит Кирилл, в C++ нет анонимных структур. Я не думаю, что обсуждение того, что было бы, если бы язык был другим, дает нам что-то :) - person Georg Fritzsche; 08.04.2010

То, что вы делаете, невозможно в стандартном C++ - определения типов не разрешены в возвращаемых типах в соответствии с §8.3.5/6 (деклараторы функций, C++03):

Типы не должны определяться в типах возврата или параметра.

В этом случае Visual Studio не соответствует требованиям.

person Georg Fritzsche    schedule 08.04.2010

Стандарт C++ не допускает анонимных структур.

person Kirill V. Lyadvinsky    schedule 08.04.2010
comment
Возможно, вы захотите сделать это более точным. 14.3.1/2 объявляет их вне закона в определенной ситуации, и очевидно, что правило необходимо только в том случае, если они в целом разрешены. - person MSalters; 08.04.2010
comment
@MSalters, Неименованные структуры обычно разрешены, но не анонимны. Первый — это тот, который поддерживает C++: struct { int i; } a; a.i = 0; (тип не имеет имени). Второй — это тот, который C++ не поддерживает: struct { int i; }; i = 0; (у типа нет имени, и он ускользает в окружающую область). - person Kirill V. Lyadvinsky; 08.04.2010

Я не могу придумать никакого разумного использования. Помимо утечек памяти, это очень нечитаемый способ достижения желаемой цели. Это заставляет вашего читателя много думать о том, что делает код. А также неизвестно, кто должен удалять 'f' в main(). И нужно ли его удалять с помощью delete[] или delete?

Я бы использовал класс, принимающий "a" и "b" в конструкторе. У него будет два метода для получения двух вычисляемых членов структуры. А внутри класса будут приватные мехатики, использующие простые циклы для вычисления того, что вы хотите. Тогда ваш API будет выглядеть так:

void main()
{
   MyCalculator myCalc(5, 6);
   double sumOfAllNumbers = myCalc.getSumOfAllNumbers();
   double product = myCalc.getProduct();
}
person m_pGladiator    schedule 08.04.2010
comment
Да, я думаю, что упомянул в своем вопросе, что это надуманный пример, и я мог бы сделать то, что он делает НАМНОГО проще, как вы предложили. Тем не менее, мой вопрос заключался в том, чтобы просто найти способ, где это могло бы быть способом делать что-то. Я считаю, что было бы удобно, если бы функция возвращала что-то, к различным частям которого я мог бы получить доступ, и мне не нужно было бы создавать специальную структуру. Например, использование: foo(5,6)-›a элегантно, и мне не нужно определять для него отдельное имя структуры. В любом случае, код не является переносимым и специфичен для VS2008. Спасибо. - person Akanksh; 08.04.2010
comment
Извините, но 'foo(5,6)-›a' может быть удобным, но я не думаю, что это элегантно. Его трудно читать, потому что обычно у вас есть такие вызовы, как object->method(). В данном случае все с точностью до наоборот: левая сторона — это функция, возвращающая структуру, а правая — просто член. Также трудно отлаживать. Но этот пример хорош для студенческих тестов;) - person m_pGladiator; 08.04.2010

Близким приближением анонимной структуры является кортеж. Boost::Tuple теперь доступен везде , и есть еще один в TR1 [который, я полагаю, распространяется с VS2008] с почти идентичным интерфейсом.

#include <boost/tuple/tuple.hpp>

template<typename T>
boost::tuple<T, T>* func(boost::tuple<T, T>* t, float a, float b ) {
    if(!t) {
      t = new boost::tuple<T, T>(a, b);
    } else {
      boost::get<0>(*t) += a;
      boost::get<1>(*t) += b;
    }
    return t;
}

boost::tuple<float, float>* foo(float a, float b) {
    if(a==0) return 0;
    return func(foo(a-1,b), a, b);
}

Как уже говорили другие, общая схема довольно ненадежна, но я хотел сосредоточиться на кортеже, а не на дизайне.

person Dennis Zickefoose    schedule 08.04.2010
comment
Хорошая дискуссия, но можете ли вы лучше уточнить, что вы имели в виду под близким приближением? (т. е. POV/намерение программиста в отличие от POV компилятора, где намерение программиста может заключаться в том, чтобы не придумывать еще одно имя типа для N-го воплощения кортежа, в результате может пострадать или улучшиться читаемость кода.) - person vladr; 08.04.2010