【问题标题】:Variadic function and C++11, passing objects as parameters可变参数函数和 C++11,将对象作为参数传递
【发布时间】:2014-08-05 12:38:20
【问题描述】:

我有这样的麻烦,当我们开始使用新的 C++11 时,编译器 (GCC) 说这段代码是错误的,但在旧版本上它可以正常工作:(

std::list<CCPoint> createPointArray(int count, ...) {
    CCPoint val;
    std::list<CCPoint*> arr;

    va_list vl;
    va_start(vl,count);
    for (int i=0;i<count;i++)
    {
        val = va_arg(vl,CCPoint*);
        arr.push_back(*val);
    }
    va_end(vl);

    return arr;
}

这就是方式,我正在使用它:

createPointArray(3, &CCPoint(1.3,2.7), &CCPoint(1.5,1.75), &CCPoint(1.9,1.3))

编译器告诉我下一个:

Error   486 error : taking address of temporary [-fpermissive] (col 57) 

为什么 &CCPoint(1.3,2.7) 有问题?如何更改此代码以使其适用于 C++11 和旧版本?

P.S.:我正在使用 Marmalade 7.3.1、Visual Studio 2010、cocos2d-x v2.2.1

【问题讨论】:

  • 为什么不简单地在任何地方使用createPointArray(3, CCPoint(1.3,2.7), CCPoint(1.5,1.75), CCPoint(1.9,1.3)) 和std::list&lt;CCPoint&gt;?
  • 您的代码无法编译 - va_arg(vl, CCPoint*) 是一个指针,因此您不能将它分配给不是指针的 val。然后你做*val 但val 不是一个指针。并且val 不能进入CCPoint * 的列表。发布你的真实代码?

标签: c++ gcc c++11 cocos2d-x marmalade


【解决方案1】:

如果您使用的是 C++11,请不要一开始就编写此函数。新的初始化列表构造函数将为您处理它:

std::list<CCPoint> l { CCPoint(1.3,2.7), CCPoint(1.5, 1.75), CCPoint(1.9, 1.3) };

如果您出于某种原因想继续使用此可变参数函数,您可以通过不获取临时地址来阻止编译器抱怨您获取临时地址。

您可以按值传递对象,尽管 ecatmur 指出这是实现定义的(如果复制和移动构造函数和析构函数都是微不足道的,应该没问题):

std::list<CCPoint> m = createPointArray(3, CCPoint(1.3,2.7),
                                        CCPoint(1.5,1.75), CCPoint(1.9,1.3));

(您已经在函数中将 val 声明为 CCPoint 实例而不是指针。只需更改 va_arg 类型以匹配)。

否则,您可以直接传递双精度值:

std::list<CCPoint> m = createPointArray(3, 1.3, 2.7, 1.5, 1.75, 1.9, 1.3);

std::list<CCPoint> createPointArray(int count, ...) {
    std::list<CCPoint> arr;

    va_list vl;
    va_start(vl,count);
    for (int i=0;i<count;i++) {
        double x = va_arg(vl,double);
        double y = va_arg(vl,double);
        arr.push_back(CCPoint(x,y));
    }
    va_end(vl);

    return arr;
}

【讨论】:

  • 通过省略号传递类类型的对象是实现定义的行为,可能不受支持;见 5.2.2p7。
  • 嗯,好点子。我没有多想那个替代方案,因为无论如何它显然是劣等的。
  • std::list&lt;CCPoint&gt; l { {1.3, 2.7}, {1.5, 1.75), {1.9, 1.3} };
【解决方案2】:

您只是无法获取临时地址,确切地说是错误。您可以通过传递值轻松解决此问题:

std::list<CCPoint> createPointArray(int count, ...) {
    std::list<CCPoint> arr;

    va_list vl;
    va_start(vl,count);
    for (int i=0;i<count;i++)
        arr.push_back(va_arg(vl,CCPoint));
    va_end(vl);

    return arr;
}

createPointArray(3, CCPoint(1.3,2.7), CCPoint(1.5,1.75), CCPoint(1.9,1.3));

而且,你现在不需要这样的功能:

std::list<CCPoint> arr {CCPoint(1.3,2.7), CCPoint(1.5,1.75), CCPoint(1.9,1.3)};

更简单

【讨论】:

  • 通过省略号传递类类型的对象是实现定义的行为,可能不受支持;见 5.2.2p7。
【解决方案3】:

获取prvalue临时地址是非法的(在C++03中也确实是非法的)。

您可以使用强制转换将值类别更改为左值:

createPointArray(3,
  &static_cast<CCPoint const&>(CCPoint(1.3,2.7)),
  &static_cast<CCPoint const&>(CCPoint(1.5,1.75)),
  &static_cast<CCPoint const&>(CCPoint(1.9,1.3)));

这在 C++03 和 C++11 中都有效,因为临时对象直到封闭的完整表达式结束时才会被销毁。

因为您通过指向const 的指针传递对象,所以您应该使用va_arg(vl, CCPoint const*) 提取它们。

【讨论】:

    【解决方案4】:

    在 C++11 中,只需使用 initializer_list:

    std::list<CCPoint> l { CCPoint(1.3, 2.7), CCPoint(1.5, 1.75), CCPoint(1.9, 1.3) };
    

    在 C++03 中,您可能有一个数组,然后使用以下命令初始化您的列表:

    const CCPoint points[] = { CCPoint(1.3, 2.7), CCPoint(1.5, 1.75), CCPoint(1.9, 1.3) };
    
    std::list<CCPoint> l(points, points + sizeof(points) / sizeof(*points));
    // or std::list<CCPoint> l(points, points + 3); // error prone
    // or std::list<CCPoint> l(std::begin(points), std::end(points)); // In C++11
    

    【讨论】:

      猜你喜欢
      • 2016-04-21
      • 2015-09-22
      • 1970-01-01
      • 1970-01-01
      • 2018-02-23
      • 2012-05-30
      • 1970-01-01
      • 1970-01-01
      • 2015-11-22
      相关资源
      最近更新 更多