g ++ c ++ 17类模板参数推导不适用于非常具体的情况

Bar*_*uch 10 c++ templates class-template template-argument-deduction c++17

我有以下代码:

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
}
Run Code Online (Sandbox Code Playgroud)

这与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> {
       ^~~
Run Code Online (Sandbox Code Playgroud)

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

示例:https://wandbox.org/permlink/jQCOhXFFQekS17Y1

Lig*_*ica 5

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

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

Bugzilla 上已经存在相关示例。


cpp*_*ner 2

编辑:此错误现已由https://gcc.gnu.org/g:5f1a2cb9c2dc09eed53da5d5787d14bec700b10b修复。


这就是我相信已经发生的事情:

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

(type) + expr
(expr) + expr
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

证明上述分析:

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

sizeof(something)(could be sizeof(type)or )中存在类似的歧义sizeof(expr),并且正如预期的那样,sizeof(lit(0))会触发类似的错误。