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

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

#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].
  • Мне нужно вернуть указатель на анонимную структуру, чтобы я мог создать экземпляр объекта в templatized function func, могу ли я сделать что-то подобное, не возвращая указатель?
  • Я думаю, что самое главное, для этого вообще ЛЮБОЙ (реальный)? В качестве примера приведенного выше утечки и, по общему признанию, изобретательны.

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

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

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

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

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

Ответы

Ответ 1

Пока ваш код не переносится; он, например, не будет строить с помощью gcc.

В разделе 14.3.1/2 стандарта говорится:

Локальный тип, тип без link, неназванный тип или тип
составленный из любого из этих типов не используется в качестве шаблона

аргумент
для шаблона типа параметра.

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

ОБНОВЛЕНИЕ 1

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

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

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

    а

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

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

ОБНОВЛЕНИЕ 2

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

ОБНОВЛЕНИЕ 3

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

 delete (int*)f;

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

Ответ 2

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

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

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

Ответ 3

Стандарт С++ не позволяет анонимные структуры.

Ответ 4

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

Я бы использовал класс, который использовал 'a' и 'b' в конструкторе. Он будет иметь два метода для получения двух вычисленных элементов структуры. И внутри класса будут личные mehotds, используя простые циклы, чтобы рассчитать то, что вы хотите. Тогда ваш API будет выглядеть так:

void main()
{
   MyCalculator myCalc(5, 6);
   double sumOfAllNumbers = myCalc.getSumOfAllNumbers();
   double product = myCalc.getProduct();
}

Ответ 5

Близкое приближение анонимной структуры является кортежем. 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);
}

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