g ++ 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
}

Это нормально компилируется с 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


person Baruch    schedule 23.10.2018    source источник
comment
еще более загадочным является то, что lit ('b') + (lit('d')) компилируется.   -  person NathanOliver    schedule 23.10.2018
comment
ошибка gcc, подана 87712   -  person Barry    schedule 23.10.2018
comment
... который вы, по-видимому, уже указали как 87709.   -  person Barry    schedule 23.10.2018
comment
@Barry Aaaa и теперь мы знаем вашу фамилию: P   -  person Lightness Races in Orbit    schedule 24.10.2018
comment
@ Lightness Не совсем то, что я старался изо всех сил скрывать.   -  person Barry    schedule 24.10.2018
comment
@BarryТоже хорошая вещь ^ _ ^   -  person Lightness Races in Orbit    schedule 24.10.2018


Ответы (2)


Это совершенно новая функция в C ++ 17 и, следовательно, новая в GCC. Наблюдаемый вами паттерн или его отсутствие очень похоже на ошибку компилятора. То, как он запускается, по-видимому, случайным образом, также соответствует этой схеме.

Углубляться в подробности того, как и почему - утомительная работа для разработчиков GCC, а не для ответа на вопрос о переполнении стека, поскольку это, вероятно, будет чрезвычайно сложным, но правильный подход сейчас - поднять ошибку и посмотреть, что произойдет. (OP уже сделал это, как ошибка 87709. )

Связанные примеры уже существуют в Bugzilla.

person Lightness Races in Orbit    schedule 23.10.2018

Изменить: эта ошибка теперь исправлена ​​https://gcc.gnu.org/dag:5f1a2cb9c2dc09cb9c2dc03dc03d5d5c06d06d5 >.


Я считаю, что произошло следующее:

Есть два вида выражений, которые выглядят одинаково, но имеют совершенно разное значение:

(type) + expr
(expr) + expr

Первое - это выражение приведения в стиле C, которое преобразует унарное выражение + expr в type; второй - бинарное выражение, выполняющее сложение.

Чтобы устранить неоднозначность выражения формы (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)) вызывает аналогичную ошибку.

person cpplearner    schedule 25.10.2018