3

我们正在创建一种域特定语言,该语言生成必须在 gcc 和 IBM xlc(AIX 版本 10.1)编译器下编译的 C++ 代码。

一个特定的代码片段生成了在 gcc 下运行良好的 C++ 代码,但在 xlc 下就不行了。我已将代码修改为仍会触发编译错误的最小情况:

//bug183.h
class emptyList
{};

extern emptyList weco;

class otherClass
{
        public:
                otherClass();
                otherClass(const int & p);
                otherClass(const emptyList & e);
                int geta() {return a;}
        private:
                int a;
};

class someClass
{
        public:
                someClass();
                someClass(const someClass & other);
                someClass(const otherClass & p1, const otherClass & p2);
                void exportState();

        private:
                otherClass oc1;
                otherClass oc2;

};

//bug183.cpp
#include "bug183.h"
#include <iostream>

emptyList weco = emptyList();

otherClass::otherClass() {a = 0;}
otherClass::otherClass(const int & p) {a = p;}
otherClass::otherClass(const emptyList & e) {a = 1000;}

someClass::someClass() {oc1 = otherClass(); oc2 = otherClass();}
someClass::someClass(const someClass & other) {oc1 = other.oc1; oc2 = other.oc2;}
someClass::someClass(const otherClass & p1, const otherClass & p2) {oc1 = p1; oc2 = p2;}
void someClass::exportState() {std::cout << oc1.geta() << " " << oc2.geta() << std::endl;}

int main()
{
        someClass dudi;
        dudi.exportState();

        //this line triggers the error in xlc
        someClass nuni = (someClass(otherClass(weco), otherClass(weco)));
        nuni.exportState();

        return 0;
}

编译它会引发以下错误:“bug183.cpp”,第 21.66 行:1540-0114 (S) 参数名称不得与此函数的另一个参数相同。

但是如果我像这样删除构造函数调用上的括号:

someClass nuni = someClass(otherClass(weco), otherClass(weco));

错误消失。此外,如果我更改另一个在错误消失时weco创建的 extern 变量,即使我将构造函数括在括号中,所以可以肯定地说,需要同时存在这两个条件才能显示此错误。weco

你们中的一些人可能会问为什么我们不直接删除括号,但这样做可能会损害部分正常工作的代码,所以我倾向于理解这种行为是否来自 C++ 编译器,或者如果至少有一个已知的解决方法。

4

1 回答 1

2

我觉得

(someClass(otherClass(weco), otherClass(weco)))

被错误地解析为cast-expression的第二次生成的开始:

强制转换表达式:
 一元表达式
 
(类型 ID)强制转换表达式

注 § 8.2 第 2 段(强调添加):

由函数样式转换和类型标识之间的相似性引起的歧义可能出现在不同的上下文中。歧义表现为函数式强制转换表达式和类型声明之间的选择。解决方案是任何在其句法上下文中可能是type-id的构造都应被视为type-id

如果您考虑cast-expressiontype-id的完整语法上下文,则in不可能match ,因为紧随其后的cast-expression不能为空。但是,如果您只考虑单令牌前瞻上下文,其中type-id必须后跟,则 8.2(2) 可能适用。不过,我并不是真的倾向于相信该标准的意图是只考虑单标记前瞻。...(..);type-id))

编辑 报告为 gcc bug 50637。您可能想向 xlc 提交类似的错误报告。

由于 gcc 4.7.2 似乎标记了与 xlc 相同的错误。我玩了一下 gcc,并说服自己,问题在于 gcc 在发现带括号的表达式不能是type-id之前标记了type-id中的错误(即两个具有相同名称的参数) 。

这是一个例子:

#include <iostream>
#include <utility>

static const int zero = 0;
int main() {
  // Consistent with 8.2[2]
  // Example 1. Invalid cast.
  // Here, 'int(int(zero))' is a type-id, so the compiler must take
  // the expression to be a cast of '+3' to a function.
  std::cout << (int(int(zero))) + 3 << std::endl;
  // Example 2: No error.
  // The parenthesized expression cannot be a type-id in this context
  std::cout << (int(int(zero))) << std::endl;
  // Example 3: Syntax error: zero redefined.
  // Here the parenthesized expression could be a type-id, so it must
  // be parsed as one, even though the type-id is invalid.
  std::cout << (std::pair<int,int>(int(zero), int(zero))) + 3 << std::endl;

  // Apparently not consistent with 8.2[2]
  // Here the parenthesized expression can't be a type-id, as in example 2.
  // However, the type-id triggers a syntax error, presumably before gcc
  // figures out that it's not a cast-expression.
  std::cout << (std::pair<int,int>(int(zero), int(zero))) << std::endl;

  return 0;
}

在 lws 上查看。(在 gcc 和 clang 之间切换以查看差异。)

基于上述分析,问题是过早触发类型错误,这是一种可能的解决方法:

1)为weco添加别名:

emptyList& weco_alias = weco;

2) 使用以下之一:

someClass nuni = (someClass(otherClass(weco), otherClass(weco_alias)));

这适用于 gcc 4.7.2 和 clang 3.2 ( lws )。

于 2013-01-18T18:04:51.933 回答