【问题标题】:temporary objects with variadic template arguments; another g++/clang++ difference带有可变模板参数的临时对象;另一个 g++/clang++ 区别
【发布时间】:2016-07-04 13:17:10
【问题描述】:

以下代码

struct foo
 {
   foo ()
    { }

   template <typename T0, typename ... Ts>
      foo (const T0 & t0, const Ts & ... ts) 
       { foo(ts...); }
 };

int main()
 {
   foo f(1, 2);

   return 0;
 }

使用g++ (4.9.2) 编译没有问题并给出以下错误

tmp_002-11,14,gcc,clang.cpp:9:16: error: expected ')'
       { foo(ts...); }
               ^
tmp_002-11,14,gcc,clang.cpp:9:13: note: to match this '('
       { foo(ts...); }
            ^
tmp_002-11,14,gcc,clang.cpp:9:14: error: redefinition of 'ts'
           { foo(ts...); }
             ^
tmp_002-11,14,gcc,clang.cpp:8:42: note: previous definition is here
      foo (const T0 & t0, const Ts & ... ts) 
                                         ^
2 errors generated.

clang++ (3.5)。

像往常一样,我的问题是:谁是对的?

--- 编辑---

澄清:我知道foo(ts...) 不能调用委托构造函数,但(我认为可以)构造一个临时的foo 对象(另一个foo 对象)。

但是,正如 vsoftco 所指出的,sizeof...(ts) == 1U 时发生了什么?

在这种情况下,foo(ts...); 是单个变量ts 的(重新)声明(所以,我想,应该是正确的clang++)或者可变参数语法避免了这个问题(所以,我想,应该对吗g++)?

有 C++11 标准专家可以澄清这一点吗?

ps:对不起,我的英语不好。

【问题讨论】:

  • 我不认为你应该被允许调用构造函数,所以 gcc 也应该拒绝它。
  • @vsoftco - 这个想法是创建一个临时对象但是......我认为问题是当它只接收一个参数时
  • 哦,我明白了,但我认为编译器相信你在那里有一个电话。不过,我不是 100% 确定。无论如何,构造函数委托应该可以正常工作:foo (const T0 &amp; t0, const Ts &amp; ... ts) : foo(ts...) {}
  • @vsoftco - 在某种程度上,你是对的:我遇到了这个问题,试图解决另一个人的问题,他正在考虑以这种方式调用委托构造函数;但我有理由确定(couted this 指针)g++ 创建另一个对象

标签: c++11 g++ language-lawyer variadic-templates clang++


【解决方案1】:

这看起来像一个 clang 错误。这是一个最小的复制:

template <typename ... Ts> void foo (Ts ... ts)
{
    int(ts...);
}

(甚至不需要实例化模板)。

从错误信息来看,clang 将int(ts...); 解释为声明,它不能是,因为(ts...) 不能是声明符。

有人可能会问:当 ts... 是一个大小为 1 的参数包时,不应该像 ts 那样解析它,从而导致整个构造被解释为声明吗?答案是否定的。

在 C++ 语法中,“声明”和“表达式语句”产生式之间存在句法歧义。如果一个模棱两可的构造可以被解析为一个“声明”,那么它就是一个“声明”。 int(ts...) 不能这样解析,因为语法没有这种解析所需的产生式。一旦构造被分类为表达式语句,而不是在此之前,我们可以将ts... 解释为参数包并计算 sizeof...(ts)。如果构造不被归类为表达式,则 sizeof...(ts) 根本没有意义,因为 ts... 没有意义,因为这样的语法不能在声明中。

一旦确定int(ts...); 是一个表达式语句而不是一个声明,就可以解释它并实例化相关模板并扩展参数包。在这一点上,基于sizeof...(ts) == 1.如果sizeof...(ts) == 1 是一个声明,则不可能推断出它(ts 那时就不是一个参数包)。

此外,clang 似乎暗示它甚至不是一个语法正确的声明。不可能从语法不正确的东西中得出像sizeof...(ts) == 1 这样的事实。

【讨论】:

  • 真的很有趣,但是如果你实例化模板(例如调用foo(1,2);),你也会收到来自g++ 的错误
  • @max66 这是一个最小的演示。它仅在您使用单个参数实例化模板时才有效。
  • 我明白了,但我不明白为什么 g++ 编译你的模板化函数但不编译 void bar (int i) { int(i); }
  • 换句话说...根据 g++,int(ts...) 是什么?如果sizeof...(ts) == 1U 应该像int(i) 那么“错误:'int i' 的声明会影响参数”; if sizeof...(ts) &gt; 1U(例如 2,对于以下示例)应该类似于 int(i, j) 所以“错误:表达式列表被视为函数转换中的复合表达式”
  • @max66 在 C++ 语法 中,“声明”和“表达式”之间存在句法歧义。如果一个模棱两可的构造可以被解析为“声明”,那么它就是“声明”。 int(ts...) 无法这样解析,因为 C++ grammar 没有此类解析所需的产生式。一旦构造被归类为表达式,而不是在此之前,您就可以开始想知道sizeof...(ts) 会是什么,因为如果构造未被归类为表达式,sizeof...(ts) 就没有意义,因为ts... 没有意义,因为这样的语法不能在声明中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-06
  • 2014-09-08
  • 2013-07-22
  • 1970-01-01
  • 2018-01-21
相关资源
最近更新 更多