【问题标题】:Is there any reasonable use of a function returning an anonymous struct?是否有任何合理使用返回匿名结构的函数?
【发布时间】:2010-04-08 07:18:06
【问题描述】:

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

#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 到a 的所有数字的总和以及a 和b 的乘积。

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

我想我只是想将匿名结构作为浮点数组删除,例如 float* f = new float[2]。正如下面的评论所暗示的,这可能是错误的,那么可以做些什么呢?我可以删除吗?

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

从答案中我相信这个装置是 VS2008 特定的实体,它不符合标准,因此不可移植。不过太糟糕了,我很想看看 Stackoverflow 或 Boost 的人想出了什么巫术,如果这在他们的武器库中:)。谢谢大家。

【问题讨论】:

  • delete[] 在用new 分配的内存上?哎哟!
  • 我知道,我只是想将匿名结构删除为浮点数组,类似于 float* f = new float[2]。
  • 我几乎肯定你根本做不到,所以有没有用并不重要。
  • @Dennis Zickefoose :我可以在 VS2008 上编译和运行它,可能会使用一些非标准扩展。但我很肯定它适用于 VS2008。
  • 尝试在delete[]之后做点什么;那里可能发生任何事情。

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


【解决方案1】:

目前,您的代码不可移植;例如,它不会使用 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. 1234563当然,除了如何为匿名结构指定构造函数? :)
  3. 除了尝试不为结构分配显式名称的极其复杂的方式外,我没有看到任何实际用途;为了不污染命名空间,通常会立即使用匿名结构(在 C++ 中技术上不合法,但各种编译器都支持它作为扩展),通常是通过立即实例化一个结构(例如,您可能会看到一次性仿函数被实例化并传递下来)作为匿名结构——同样,技术上不合法的 C++。)

更新 2

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

更新 3

把这个从 cmets 拿出来:在分配有 new(而不是 new[])的内存上调用 delete[] 是对堆损坏的邀请。在您不知道其类型在技术上未定义的指针上调用 delete(应该调用哪个析构函数?)但是对于 POD(您的匿名结构是一个),您可以在这个 horrible 骇人听闻的方式:

 delete (int*)f;

当然,如果您的代码具有神奇的格式,std::auto_ptr 将能够保留匿名类型,并会为您正确而优雅地调用 delete

【讨论】:

  • 感谢在这种情况下可能的演变。
  • @Vlad:即使 N2657 集成了它的不可移植性,因为返回类型中不允许类型定义。
  • @gf,是的,当我说 assuming your code were well-formed 时,这是一个超出 N2657 的大假设,但为了争论,我们假设 g++ 也支持它作为未来的扩展(@ 987654337@ 在构建 C 时会很好地吞下它,就像 MSVC 为 OP 所做的那样......)感谢标准参考 BTW! :)
  • 更新前写的。即便如此,正如基里尔所说,C++ 中没有匿名结构。我不认为讨论如果语言不同会怎样:)
【解决方案2】:

您所做的事情在标准 C++ 中是不可能的 - 根据 §8.3.5/6(函数声明符,C++03),返回类型中不允许类型定义:

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

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

【讨论】:

    【解决方案3】:

    C++ 标准不允许匿名结构。

    【讨论】:

    • 您可能希望更精确。 14.3.1/2 在特定情况下禁止它们,很明显,只有在普遍允许的情况下才需要规则。
    • @MSalters,未命名的结构 通常是允许的,但不是匿名的。第一个是 C++ 支持的这个:struct { int i; } a; a.i = 0;(类型没有名称)。第二个是这个,C++ 不支持的:struct { int i; }; i = 0;(类型没有名字,它逃到了周围的作用域)。
    【解决方案4】:

    我想不出任何合理的用法。除了内存泄漏之外,这是实现您想要的目标的一种非常难以理解的方式。它让你的读者思考了很多代码在做什么。而且也不知道谁应该删除 main() 中的“f”。应该用delete[]删除还是delete删除?

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

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

    【讨论】:

    • 是的,我想我已经在我的问题中提到这是一个人为的例子,我可以按照您的建议以更简单的方式完成它。然而,我的问题只是找到一种方法,这可能是做事的方式。我相信如果一个函数返回的东西我可以访问它的各个部分而不必制作一个特殊的结构,那将会很方便。例如用法 : foo(5,6)->a 很优雅,我不必为它定义单独的结构名称。无论如何,代码是不可移植的,它是 VS2008 特定的。谢谢。
    • 抱歉,'foo(5,6)->a' 可能很方便,但我不认为它很优雅。它很难阅读,因为通常你有像“object->method()”这样的调用。在这种情况下,情况正好相反——左侧是一个返回结构的函数,而右侧只是一个成员。也很难调试。但是这个例子很适合学生测试;)
    【解决方案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);
    }
    

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

    【讨论】:

    • 很好的讨论,但你能更好地说明你所说的“近似”是什么意思吗? (即程序员 POV/intent 与编译器 POV 不同,程序员的意图可能是不必为元组的第 N 个化身想出另一个类型名称,这可能会导致代码可读性受损或蓬勃发展。)跨度>
    猜你喜欢
    • 2019-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-12
    • 1970-01-01
    相关资源
    最近更新 更多