是否有合理使用函数返回匿名结构?

时间:2010-04-08 07:18:06

标签: c++ templates memory-leaks anonymous-types

这是一个使用函数的(人工)示例,该函数返回一个匿名结构并执行“有用”:

#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. 我想最重要的是,是否有任何(现实世界)用于此?正如上面给出的例子泄漏并且无可否认的做法。
  4. 顺便说一下,函数foo(a,b)的作用是返回一个包含两个数字的结构,即从1到a的所有数字的总和以及a和b的乘积。

    也许行new T可以使用boost :: shared_ptr以某种方式避免泄漏,但我没有尝试过。那会有用吗?

    我想我只是试图将匿名结构删除为浮点数组,类似于float * f = new float [2]。哪个可能是错的,正如下面的评论所示,那么可以做些什么呢?我可以删除吗?

    我可以在VS2008上“按原样”编译和运行此代码,VS可能正在使用某些非标准扩展,但它确实运行并给出15和30作为答案。

    从答案中我相信这个装置是VS2008特定的实体,它不符合标准,因此不便携。太糟糕了,我本来希望看到Stackoverflow或Boost人们想出的伏都教,如果这是他们的武器库:)。谢谢大家。

5 个答案:

答案 0 :(得分:9)

目前,您的代码不可移植;例如,它不会与gcc构建。

标准的第14.3.1 / 2节说:

  

本地类型,没有的类型   链接,未命名类型或类型
  从任何这些类型复合而成   不得用作模板 -
  参数
用于模板   型参数。

有关可能的演变,请参阅C++ Standard Core Language Defect Reports, Revision 69Paper N2657中的第488项。

更新1

假设您的代码格式正确,那么:

  1. 您可以重写:

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

    作为

    std::cout << std::auto_ptr(foo(5,6))->a << std::endl;
    
  2. 如果匿名结构具有另一种类型的构造函数(匿名或不是匿名,您可以在方法体内初始化),则可以返回匿名struct按值 - 当然,除了如何为匿名结构指定构造函数之外? :)

  3. 除了一种极其错综复杂的尝试不为结构指定明确名称的方式之外,我看不到真实世界的使用;一个人通常会使用匿名结构(在C ++中不是技术上合法的,但是由各种编译器支持作为扩展)以便不污染命名空间,通常是通过立即实例化(例如,您可以看到一次性仿函数被实例化并传递下来作为匿名结构 - 再次,技术上不合法的C ++。)

  4. 更新2

    感谢gf指向C ++标准相关部分的链接,该部分涉及可能未在返回类型中定义的新类型。

    更新3

    从评论中提到这一点:在分配了delete[](而不是new)的内存上调用new[]是对堆损坏的邀请。在一个你不知道的类型的指针上调用delete在技术上是未定义的(应该调用哪个析构函数?)但是在POD(你的匿名结构是一个)的情况下你可以在这个中逃脱它可怕的 hackish方式:

     delete (int*)f;
    

    当然,如果您的代码神奇地构造良好,std::auto_ptr本来可以保留匿名类型,并且会正确而优雅地为您调用delete

答案 1 :(得分:5)

您在标准C ++中无法做到 - 根据§8.3.5/ 6 (函数声明符,C ++ 03),返回类型中不允许使用类型定义:

  

不应在返回或参数类型中定义类型。

在这种情况下,Visual Studio不兼容。

答案 2 :(得分:3)

C ++ Standard不允许使用匿名结构。

答案 3 :(得分:1)

我想不出任何合理的用法。除了内存泄漏,这是实现您想要的目标的一种非常难以理解的方式。它使您的读者能够充分考虑代码的作用。而且还不知道谁应该删除main()中的'f'。它应该用delete []删除还是删除?

我会在构造函数中使用一个带'a'和'b'的类。它将有两种方法来获取两个计算的struct成员。在课堂内将有私人mehotds,使用普通循环来计算你想要的东西。然后您的API将如下所示:

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

答案 4 :(得分:1)

匿名结构的近似是一个元组。 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);
}

正如其他人所说,整体方案非常脆弱,但我想专注于元组而不是设计。