为什么在临时对象上调用函数操作符有时会被解释为不遮蔽参数的声明?

3
我的问题与这个已解答的问题类似,但有一个重要的区别。
如果我尝试编译以下代码:
void test(string x){
  int x = 5;
  cout << x << endl;
}

我收到了预期的警告:

In function ‘void test(std::__cxx11::string)’:
error: declaration of ‘int x’ shadows a parameter
    int x = 5;
        ^

现在来看我的问题的核心; 如果我有一个类型,其中包含如下所示的函数调用运算符:
struct foo{
  string bar;
  foo(const string& bar) : bar(bar) { }
  void operator()(){
    cout << "my bar is \"" << bar << '"' << endl;
  }
};

我可以创建一个临时对象,并使用foo("this will print")();foo{"this will also print"}(); 调用它的 () 操作符。

如果我尝试执行以下操作,将会得到编译器错误,这是可以通过上面链接的帖子进行解释的。

void redeclaration_error(){
  string x("lol");
  foo(x)();
}

然而,在这里事情变得奇怪:
void hmm1(string str){
  foo(str)();
}
void hmm2(string str){
  foo{str}();
}

虽然调用时,hmm2会输出其参数,但hmm1什么都不做。这是预期的,因为hmm1的主体除了声明str是类型为foo的变量外,什么也不做。但是,在hmm1的范围内,str已经被声明为函数参数的类型string,那么为什么这不会导致编译器错误,例如error: declaration of ‘foo str‘ shadows a parameter
我理解编译器将foo(str)();解释为一个声明,而像foo(str).operator()();(foo(str))();一类的行将被解释为创建临时的foo对象并调用它们的函数调用运算符,但是在这种情况下,使用与参数相同的名称声明变量是否可以接受呢?
这里有一个我玩弄此事的Ideone链接。

1
似乎是gcc的Bug,clang报了一个错误:https://godbolt.org/z/ECiUdI - R2RT
1
MSVC 也会拒绝它。 - aschepler
1
你应该报告这个问题,看起来肯定是一个 bug。 - XapaJIaMnu
1
@JeffreyCash 嗯,看起来正确。 - M.M
1
好的,我根据评论发布了一个答案,并相应地将其标记为“社区”。 - M.M
显示剩余7条评论
1个回答

1

一个更简单的程序产生相同的问题(额外的括号是个误导):

void f(int x)
{
    void x();
}

这是不正确的,因为基于C++17 [basic.scope.block]/2的规定: [...] 参数名不得在函数定义的最外层块中重新声明,也不得在任何与 function-try-block 相关联的处理程序的最外层块中重新声明。 如果编译器接受此代码,则是编译器错误。

太棒了,将其简化到最简单的形式!我会尽快提交错误报告。我很惊讶这个问题在GCC中存在了这么长时间,在我检查的4.4.7和8.3版本中都有它。 - Jeffrey Cash

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接