Вот (искусственный) пример использования функции, которая возвращает анонимную структуру и делает "что-то" полезное:
#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;
}
Существует несколько моментов, которые я хотел бы обсудить:
func
, я могу сделать что-то подобное, не возвращая указатель?Между прочим, что функция foo(a,b)
делает, для возврата структуры, содержащей два числа, сумму всех чисел от 1 до a и продукта a и b.
Возможно, строка new T
мог использовать повышение:: shared_ptr так или иначе для предотвращения утечек но я не попробовал это. Это работало бы?
Я думаю, что просто пытался удалить анонимную структуру как массив плаваний, чего-то как плавание* f = новое плавание [2]. Который мог бы быть неправильным, поскольку комментарий ниже предлагает, поэтому что может быть сделано? я могу удалить вообще?
Я могу скомпилировать и выполнить этот код "как есть" VS2008, возможно, некоторые нестандартные расширения могли бы использоваться VS, но он действительно выполняет и дает 15 и 30 как ответ.
Из ответов я полагаю, что это хитрое изобретение является определенным объектом VS2008, это не совместимые стандарты и таким образом не портативное. Слишком плохо, хотя, мне понравилось бы видеть, какой вуду люди Stackoverflow или Boost придумали, если это было в их арсенале :). Спасибо все.
Пока что ваш код не переносится; он, например, не будет собираться с gcc
.
Раздел 14.3.1/2 стандарта гласит:
Локальный тип, тип, не имеющий связи, неназванный тип или тип
, составленный из любого из этих типов не должен использоваться в качестве шаблона-
аргумента для шаблона тип-параметр.
См. пункт 488 в C++ Standard Core Language Defect Reports, Revision 69 и Paper N2657 для одной из возможных эволюций.
Если предположить, что ваш код был хорошо сформирован, то:
вы можете переписать:
std::cout << foo(5,6)->a << std::endl;
как
std::cout << std::auto_ptr(foo(5,6))->a << std::endl;
вы можете вернуть анонимную struct
по значению при условии, что анонимная struct имеет конструктор, принимающий другой тип (анонимный или нет, который вы сможете инициализировать в теле вашего метода) -- только, конечно, как вы укажете конструктор для анонимной struct? :)
никакого реального применения, кроме крайне запутанного способа не присваивать явное имя структуре; обычно используются анонимные структуры (технически не законные в C++, но поддерживаемые различными компиляторами как расширения), чтобы не загрязнять пространство имен, обычно путем инстанцирования сразу (например, вы можете увидеть одноразовые функторы, инстанцируемые и передаваемые как анонимные структуры - опять же, технически не законные в C++. )
Спасибо gf
за ссылку на соответствующую часть стандарта C++, касающуюся новых типов, которые не могут быть определены в возвращаемом типе.
Выношу это сюда из комментариев: вызов delete[]
на памяти, выделенной с помощью new
(в отличие от new[]
) - это приглашение к повреждению кучи. Вызов delete
на указателе, тип которого вам неизвестен, технически не определен (какой деструктор должен быть вызван? ), но в случае с POD (а ваш анонимный struct является таковым) вы можете избежать этого таким ужасным хакерским способом:
delete (int*)f;
Конечно, если бы ваш код был магически хорошо сформирован, std::auto_ptr
смог бы сохранить анонимный тип и позаботился бы о корректном и изящном вызове delete
для вас.
То, что вы делаете, невозможно в стандартном C ++ - определения типов не разрешены в возвращаемых типах согласно §8.3.5 / 6 (деклараторы функций, C ++ 03):
Типы не должны определяться в возвращаемых типах или типах параметров.
Visual Studio в этом случае несовместима.
Я не могу придумать разумного использования. Помимо утечек памяти, это очень нечитаемый способ достижения желаемой цели. Это заставляет вашего читателя много думать о том, что делает код. А также неизвестно, кто должен удалять 'f' в main (). И следует ли его удалить с помощью delete [] или delete?
Я бы использовал класс, принимающий 'a' и 'b' в конструкторе. У него будет два метода получения двух вычисляемых членов структуры. А внутри класса будут частные методы, использующие простые циклы для вычисления того, что вы хотите. Тогда ваш API будет выглядеть так:
void main()
{
MyCalculator myCalc(5, 6);
double sumOfAllNumbers = myCalc.getSumOfAllNumbers();
double product = myCalc.getProduct();
}
Точное приближение анонимной структуры - это кортеж. 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);
}
Как уже говорили другие, общая схема довольно хрупкая, но я хотел сосредоточиться на кортеже, а не на дизайне.