Почему объявления функций-членов шаблона класса должны быть правильно сформированы? - PullRequest
1 голос
/ 22 октября 2010

ОК, предположим, я хочу проверить, имеет ли параметр шаблона вложенный тип / typedef XYZ.

template <class T>
struct hasXZY
{
   typedef char                  no;
   typedef struct { char x[2]; } yes;
   template <class U>
   static yes f(typename U::XYZ*);
   template <class /*U*/>
   static no  f(...);
   enum {value = sizeof(f<T>(0))==sizeof(yes)};
};

Работает нормально, как и ожидалось.

Теперь рассмотрим это:

template <class T>
struct hasXZY
{
   typedef char                  no;
   typedef struct { char x[2]; } yes;

   static yes f(typename T::XYZ*);
   static no  f(...);
   enum {value = sizeof(f(0))==sizeof(yes)};
};

hasXYZ<int> теперь приводит к ошибке во время компиляции.ОК, f не является функцией шаблона.Но с другой стороны, когда hasXYZ создается для int через hasXYZ<int>::value, компилятор может легко исключить f(int::XYZ*) из списка кандидатов.Я просто не понимаю, почему ошибка при создании объявления функции-члена в шаблоне класса должна привести к сбою создания всего класса.Любые идеи?

Редактировать: Мой вопрос: почему объявления функции-члена должны быть все правильно сформированы?Поскольку компилятор создает экземпляры методов только после их использования, зачем ему нужно правильное объявление.Рассмотрим приведенный выше пример2 как возможный вариант использования этой функции.

Ответы [ 3 ]

4 голосов
/ 22 октября 2010

SFINAE используется только при создании набора кандидатов для разрешения перегрузки функции. В первом примере вы вызываете перегруженную функцию f (), а первая исключена благодаря SFINAE.

Во втором примере, когда создается экземпляр hasXZY, все его члены должны быть четко определены, и подстановка параметра шаблона не должна завершиться ошибкой. Это делает для int :: XYZ.

Члены не будут исключены из класса из-за ошибки замещения.

3 голосов
/ 23 октября 2010

Я не адвокат по языку C ++, но я попробую.

Во втором примере функции-члены должны быть четко определены, поскольку они больше не являются шаблонными функциями после создания экземпляра hasXZY для int. Чтобы убедиться в этом, сделайте замену T «от руки»:

struct hasXYZ
{
    typedef int                   T;
    typedef char                  no;
    typedef struct { char x[2]; } yes;

    static yes f(T::XYZ*);
    static no  f(...);
    enum {value = sizeof(f(0))==sizeof(yes)};
};

int main()
{
    std::cout << hasXYZ::value << "\n";
}

и обратите внимание, что это не скомпилируется, с той же ошибкой компилятора, что и раньше (по крайней мере, в GCC):

foo.cc:9: error: ‘T’ is not a class or namespace

Напротив, первый пример компилируется и ведет себя как ожидалось после ручного создания экземпляра; f члены по-прежнему настроены на U.

2 голосов
/ 23 октября 2010

Edit: Мой вопрос: почему объявления функций-членов должны быть все правильно сформированы? Поскольку компилятор создает экземпляры методов только после их использования, зачем ему нужно правильное объявление. Рассмотрим приведенный выше пример2 как возможный вариант использования этой функции.

При неявном создании экземпляра специализации шаблона класса, компилятор должен проверять полный декларатор этого члена, потому что он должен знать основную информацию об объявлении. Такое может способствовать размеру специализации шаблона класса.

Если проверка части объявления обнаружит, что она объявляет элемент данных, значение sizeof класса может привести к другому значению. Если бы вы объявили вместо этого указатель на функцию, это было бы так

yes (*f)(typename T::XYZ*);

Язык C ++ определен таким образом, что тип объявления известен только после анализа всего объявления.

Вы можете утверждать, что вы поместили статический сигнал туда, и, таким образом, в этом случае это не требуется для вычисления его размера. Но необходим для поиска по имени, чтобы знать, к чему относится имя hasXZY<T>::f и что вообще было объявлено имя f . Компилятор не создает определение hasXYZ::f, но only создает экземпляр неопределяемой части объявления, чтобы получить его тип и добавить его имя к типу класса. для поиска имени. Я полагаю, что поддержка отложенного создания экземпляров для объявления имен в конкретных случаях, когда это может сработать, еще больше усложнит реализацию компиляторов C ++ и спецификации C ++, не принося сравнимых преимуществ.

И, наконец, в вашем примере, когда вы пытаетесь вызвать его, компилятор имеет для создания экземпляра объявления, потому что ему нужно найти имя f, а для этого нужно чтобы узнать, является ли это объявление функцией или чем-то еще. Поэтому я даже теоретически не могу понять, как ваш пример мог бы работать без создания декларации. Обратите внимание, что в любом случае они , а не будут создавать определение функции.

...