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

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

#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. Я должен возвратить указатель на анонимную структуру, таким образом, я могу создать экземпляр объекта в функции templatized func, я могу сделать что-то подобное, не возвращая указатель?
  3. Я предполагаю самое важное, есть ли КАКОЕ-ЛИБО (реальное) использование для этого вообще? Как пример, данный выше утечек и, по общему признанию изобретен.

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

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

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

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

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

7
задан rekire 2 May 2012 в 18:39
поделиться

5 ответов

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

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

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

См. пункт 488 в C++ Standard Core Language Defect Reports, Revision 69 и Paper N2657 для одной из возможных эволюций.

UPDATE 1

Если предположить, что ваш код был хорошо сформирован, то:

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

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

    как

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

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

UPDATE 2

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

UPDATE 3

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

 delete (int*)f;

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

9
ответ дан 6 December 2019 в 09:59
поделиться

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

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

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

5
ответ дан 6 December 2019 в 09:59
поделиться

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

3
ответ дан 6 December 2019 в 09:59
поделиться

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

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

void main()
{
   MyCalculator myCalc(5, 6);
   double sumOfAllNumbers = myCalc.getSumOfAllNumbers();
   double product = myCalc.getProduct();
}
1
ответ дан 6 December 2019 в 09:59
поделиться

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

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

1
ответ дан 6 December 2019 в 09:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: