Потенциальная ошибка шаблона g ++? - PullRequest
12 голосов
/ 13 января 2011

Я столкнулся с кодом, который я думаю должен скомпилировать, но это не так.Поэтому я надеюсь, что некоторые местные эксперты по стандартизации здесь, в SO, могут помочь: -).

У меня есть некоторый код, похожий на этот:

#include <iostream>

template <class T = int>
class A {
public:
    class U {
    };

public:
    U f() const { return U(); }
};

// test either the work around or the code I want...
#ifndef USE_FIX
template <class T>
bool operator==(const typename A<T>::U &x, int y) {
    return true;
}
#else
typedef A<int> AI;
bool operator==(const AI::U &x, int y) {
    return true;
}
#endif

int main() {
    A<int> a;
    std::cout << (a.f() == 1) << std::endl;
}

Итак, для описания того, чтоздесь происходитУ меня есть шаблон класса (A), который имеет внутренний класс (U) и, по крайней мере, одну функцию-член, которая может возвращать экземпляр этого внутреннего класса (f()).

Тогда япопытка создать функцию operator==, которая сравнивает этот внутренний тип с каким-либо другим типом (в данном случае int, но это, похоже, не имеет значения).

Когда USE_FIX равен не определено Я получаю следующую ошибку:

test.cc: In function 'int main()':
test.cc:27:25: error: no match for 'operator==' in 'a.A<T>::f [with T = int]() == 1'

Что кажется странным, потому что я явно (я думаю) определяю шаблонное operator==, которое должно охватывать это, на самом деле, если я просто сделаюнемного работы для компилятора (включите USE_FIX), тогда я больше не получаю ошибку.К сожалению, «исправление» не работает вообще, только для конкретного экземпляра шаблона.

Должно ли это работать, как я ожидал?Или это просто не разрешено?

Кстати: если это имеет значение, я использую gcc 4.5.2.

Ответы [ 3 ]

15 голосов
/ 13 января 2011

Проблема с const typename A<T>::U &x заключается в том, что U является зависимым типом, и компилятор не может вывести T из аргумента (это один из не выводимых контекстов).

Вы можете, например,, иметь две специализации A:

class X { };
class Y { };
class Z { };

template <> class A<X> {
public: 
    typedef Z U;
};

template <> class A<Y> {
public:
    typedef Z U;
};

Если вы затем вызовете:

Z a;
a == 1;

, что должен выводить компилятор T как?X?Y?

Одним из решений в данном конкретном случае является объявление operator== как друга без шаблонов внутри шаблона класса:

template <class T = int>
class A {
public:
    class U {
    };

    friend bool operator==(const U& x, int y) {
        return true;
    }

public:
    U f() const { return U(); }
};
11 голосов
/ 13 января 2011
template <class T>
bool operator==(const typename A<T>::U &x, int y) {
    return true;
}

При использовании этого шаблона недопустимо (или иногда возможно) выводить параметр шаблона T из типа x.Это то, что известно как не выводимый контекст.(Например, кто-то может специализировать A для другого параметра, скажем double и сделать A<double>::U typedef для A<int>::U.)

Обходного пути нет, вам придется явно указать параметр шаблонакоторый для operator== делает для уродливого синтаксиса.

4 голосов
/ 13 января 2011

Это не допускается по достаточно очевидным причинам. В общем случае компилятор не может получить аргумент шаблона из вашего вызова оператора ==. Очевидно, вы предполагали, что вложенный тип U однозначно определяет специализацию A. Это не так, что можно проиллюстрировать на следующем примере с двумя явными специализациями A

template <> class A<int> {
public:
  class U {};
};

template <> class A<double> {
public:
  typedef A<int>::U U;
};

В этом случае, если вы вызываете шаблонный оператор == с аргументом типа A<int>::U, компилятор не может вывести аргумент шаблона T для шаблонного оператора ==. T должно быть int или double? Там нет никакого способа сказать.

Чтобы избежать этих двусмысленностей, такие ситуации называются не выведенными контекстами . Вывод аргументов шаблона класса вложенности из вложенного типа является примером невнедренного контекста.

...