[英]g++ c++17 class template argument deduction not working in a very specific case
I have the following code:我有以下代码:
template <class T>
class lit {
public:
lit(T l) : val(l) {}
T val;
};
template <class T>
class cat {
public:
cat(lit<T> const& a, lit<T> const& b) : a(a), b(b) {}
lit<T> const& a;
lit<T> const& b;
};
template <class T>
cat<T> operator+(lit<T> const& a, lit<T> const& b) {
return cat(a, b);
}
int main() {
auto r1 = cat((lit ('b')), lit('d')); // compiles
auto r2 = (lit ('b')) + lit('d') ; // doesn't compile
auto r3 = lit ('b') + lit('d') ; // compiles
auto r4 = (lit ('b')) ; // compiles
auto r5 = (lit<char>('b')) + lit('d') ; // compiles
}
This compiles fine with clang (as I would expect), but gcc produces the following error:这与 clang 编译得很好(正如我所期望的),但 gcc 产生以下错误:
prog.cc: In function 'int main()':
prog.cc:23:20: error: missing template arguments after 'lit'
auto r2 = (lit ('b')) + lit('d') ; // doesn't compile
^~~
prog.cc:2:7: note: 'template<class T> class lit' declared here
class lit : public ExpressionBuilder<T> {
^~~
It seems to not be able to figure out the class template deduction from the constructor only in one very specific case ( r2
).似乎仅在一种非常特定的情况下(
r2
)无法从构造函数中推断出类模板。 I am assuming gcc is wrong, but can someone explain why it would fail only in this very specific case?我假设 gcc 是错误的,但有人可以解释为什么它只会在这种非常特殊的情况下失败吗?
Example here: https://wandbox.org/permlink/jQCOhXFFQekS17Y1这里的例子: https : //wandbox.org/permlink/jQCOhXFFQekS17Y1
This is a brand new feature in C++17, and therefore brand new in GCC.这是 C++17 中的一个全新特性,因此在 GCC 中也是全新的。 The pattern you've observed — or lack thereof — looks very much like a compiler bug.
您观察到的模式——或缺乏模式——看起来非常像一个编译器错误。 The way that it is triggered apparently randomly also fits that pattern.
它显然是随机触发的方式也符合这种模式。
Delving further into the exact hows and whys is a tedious job for the GCC devs, not for a Stack Overflow answer, as it's likely to be extraordinarily complex… but the correct approach now is to raise a bug and watch what happens.深入研究确切的方法和原因对于 GCC 开发人员来说是一项乏味的工作,而不是 Stack Overflow 的答案,因为它可能非常复杂……但现在正确的方法是提出错误并观察会发生什么。 (OP has now done that, as bug 87709 .)
(OP 现在已经这样做了,因为错误 87709 。)
Related examples do already exist on Bugzilla . Bugzilla 上已经存在相关示例。
Edit: this bug is now fixed by https://gcc.gnu.org/g:5f1a2cb9c2dc09eed53da5d5787d14bec700b10b .编辑:这个错误现在由https://gcc.gnu.org/g:5f1a2cb9c2dc09eed53da5d5787d14bec700b10b修复。
This is what I believe has happened:这是我认为已经发生的事情:
There are two kinds of expressions that look similar but have vastly different meaning:有两种看起来相似但含义却截然不同的表达方式:
(type) + expr
(expr) + expr
The first is a C-style cast expression, that converts the unary expression + expr
to type
;第一个是 C 风格的强制转换表达式,它将一元表达式
+ expr
转换为type
; the second is a binary expression that performs addition.第二个是执行加法的二进制表达式。
To disambiguate an expression of form (something) + expr
, GCC first assumes that something
is a type and does a tentative parse.为了消除 form
(something) + expr
表达式的歧义,GCC 首先假设something
是一种类型并进行试探性解析。 If that succeeds, then the whole expression is treated as a cast expression;如果成功,则将整个表达式视为强制转换表达式; otherwise,
something
is reparsed as an expression.否则,
something
被重新解析为表达式。
Now here's where I think the bug resides: during the tentative parse, GCC wrongly believes that class template argument deduction (CTAD) cannot appear, so it issues an error when CTAD appears.现在这里是我认为错误所在的地方:在暂定解析期间,GCC 错误地认为类模板参数推导(CTAD)不能出现,因此当 CTAD 出现时它会发出错误。 But in fact, even though the tentative parse will definitely fail in this case,
something
may still be a valid function-style cast expression, and thus the reparse might be successful.但实际上,即使在这种情况下试探性解析肯定会失败,但
something
可能仍然是有效的函数式强制转换表达式,因此重新解析可能会成功。
For cat((lit('b')), lit('d'))
, lit('b') + lit('d')
, and (lit('b'))
, GCC is clever enough to see that they can't be C-style cast expression, so it does not do the tentative parse.对于
cat((lit('b')), lit('d'))
, lit('b') + lit('d')
和(lit('b'))
,GCC 足够聪明,可以看到它们不能是 C 风格的强制转换表达式,所以它不做试探性解析。 For (lit<char>('b')) + lit('d')
, there's no CTAD in lit<char>('b')
, so it is fine as well.对于
(lit<char>('b')) + lit('d')
, lit<char>('b')
没有 CTAD,所以也很好。
Prove of the above analysis:上述分析的证明:
If +
is changed to /
(or most operators other than -
, *
or &
), no error occurs, because (something) / expr
can't be a valid cast expression.如果
+
更改为/
(或除-
、 *
或&
之外的大多数运算符),则不会发生错误,因为(something) / expr
不能是有效的转换表达式。
Similar ambiguity exists in sizeof(something)
(could be sizeof(type)
or sizeof(expr)
), and as expected, sizeof(lit(0))
triggers a similar error.类似的歧义存在于
sizeof(something)
(可能是sizeof(type)
或sizeof(expr)
),正如预期的那样, sizeof(lit(0))
会触发类似的错误。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.