繁体   English   中英

g++ c++17 类模板参数推导在非常特殊的情况下不起作用

[英]g++ c++17 class template argument deduction not working in a very specific case

我有以下代码:

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
}

这与 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> {
       ^~~

似乎仅在一种非常特定的情况下( r2 )无法从构造函数中推断出类模板。 我假设 gcc 是错误的,但有人可以解释为什么它只会在这种非常特殊的情况下失败吗?

这里的例子: https : //wandbox.org/permlink/jQCOhXFFQekS17Y1

这是 C++17 中的一个全新特性,因此在 GCC 中也是全新的。 您观察到的模式——或缺乏模式——看起来非常像一个编译器错误。 它显然是随机触发的方式也符合这种模式。

深入研究确切的方法和原因对于 GCC 开发人员来说是一项乏味的工作,而不是 Stack Overflow 的答案,因为它可能非常复杂……但现在正确的方法是提出错误并观察会发生什么。 (OP 现在已经这样做了,因为错误 87709 。)

Bugzilla 上已经存在相关示例。

编辑:这个错误现在由https://gcc.gnu.org/g:5f1a2cb9c2dc09eed53da5d5787d14bec700b10b修复。


这是我认为已经发生的事情:

有两种看起来相似但含义却截然不同的表达方式:

(type) + expr
(expr) + expr

第一个是 C 风格的强制转换表达式,它将一元表达式+ expr转换为type 第二个是执行加法的二进制表达式。

为了消除 form (something) + expr表达式的歧义,GCC 首先假设something是一种类型并进行试探性解析。 如果成功,则将整个表达式视为强制转换表达式; 否则, something被重新解析为表达式。

现在这里是我认为错误所在的地方:在暂定解析期间,GCC 错误地认为类模板参数推导(CTAD)不能出现,因此当 CTAD 出现时它会发出错误。 但实际上,即使在这种情况下试探性解析肯定会失败,但something可能仍然是有效的函数式强制转换表达式,因此重新解析可能会成功。

对于cat((lit('b')), lit('d')) , lit('b') + lit('d')(lit('b')) ,GCC 足够聪明,可以看到它们不能是 C 风格的强制转换表达式,所以它不做试探性解析。 对于(lit<char>('b')) + lit('d')lit<char>('b')没有 CTAD,所以也很好。

上述分析的证明:

如果+更改为/ (或除-*&之外的大多数运算符),则不会发生错误,因为(something) / expr不能是有效的转换表达式。

类似的歧义存在于sizeof(something) (可能是sizeof(type)sizeof(expr) ),正如预期的那样, sizeof(lit(0))会触发类似的错误。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM