CRTP терпит неудачу w / decltype - PullRequest
       28

CRTP терпит неудачу w / decltype

5 голосов
/ 19 ноября 2010
template<typename T> struct A {
    auto func() -> decltype(T::func()) {
        return T::func();
    }
};
class B : public A<B> {
    void func() {
    }
};

Кажется, довольно просто для меня. Но MSVC не скомпилируется.

visual studio 2010\projects\temp\temp\main.cpp(4): error C2039: 'func' : is not a member of 'B'
visual studio 2010\projects\temp\temp\main.cpp(8) : see declaration of 'B'
visual studio 2010\projects\temp\temp\main.cpp(8) : see reference to class template instantiation 'A<T>' being compiled
          with
          [
              T=B
          ]
visual studio 2010\projects\temp\temp\main.cpp(4): error C3861: 'func': identifier not found

Даже если компилятор с радостью примет вызов функции. Приведенный ниже пример компилируется нормально.

template<typename T> struct A {
    void func() {
        return T::func();
    }
};
class B : public A<B> {
    void func() {
    }
};

У меня та же проблема при попытке использовать любые типы из аргумента шаблона.

template<typename T> struct A {
    typedef typename T::something something;
};
class B : public A<B> {
    typedef char something;
};

visual studio 2010\projects\temp\temp\main.cpp(4): error C2039: 'something' : is not a member of 'B'

Принимая во внимание, что класс B четко определяет тип, называемый «что-то». Компилятор очень рад вызывать функции для объектов типа T, T & или T *, но я не могу получить доступ к любым типам из T.

Ответы [ 3 ]

3 голосов
/ 21 ноября 2010

Вы пытаетесь использовать T::func до того, как оно было объявлено.Вот почему компилятор кричит на вас.Обратите внимание, что когда вы наследуете класс, класс генерируется, если он исходит из шаблона класса.А неявная генерация класса (которая называется неявной реализацией) требует генерации объявлений для всех его членов (поэтому компилятор знает значение sizeof класса и может выполнять поиск в нем).

Таким образом, он также создает экземпляр объявления auto func() -> decltype(T::func()) и, безусловно, терпит неудачу.

2 голосов
/ 21 ноября 2010

Кажется, с вашим кодом несколько проблем, одна из которых выглядит как ошибка VS10.

  1. Вы звоните T::func() с A без приведения A к T, это необходимо как часть CRTP, поскольку A не происходит от T. - FixL return static_cast<T*>(this)->func();
  2. То, что вы передаете decltype, выглядит как статический вызов функции, тогда как func фактически является функцией экземпляра. Поскольку decltype не на самом деле запускает функцию, вы должны сделать что-то вроде этого decltype(static_cast<T*>(nullptr)->func())
  3. func является приватным в B и не может вызываться с A - Исправлено: измените A на struct
  4. Это похоже на ошибку в VS10, даже после всех этих исправлений я получаю сообщение об ошибке, что вы пытаетесь использовать неопределенный класс B в decltype.

В качестве обходного пути вы можете преобразовать func в базовый класс? (теперь нам нужны два параметра шаблона, один для приведения и один для decltype, что создает новую идиому CRTPEX)

struct Base { 
    void func() { }
};

template<typename T, typename U> struct A {
    auto func() -> decltype(static_cast<T*>(nullptr)->func()) {
        return static_cast<U*>(this)->func();
    }
};


struct B : public A<Base, B>, public Base {
};

Я вижу, что g ++ также задыхается от этого decltype Кто-нибудь может подтвердить, что это дефект? Если так, я открою ошибку для Microsoft. Насколько я понимаю, следующий код допустим, но ни g ++, ни VC10 его не компилируют.

template<typename T> struct A {
    auto func() -> decltype(static_cast<T*>(nullptr)->func()) {
        return static_cast<T*>(this)->func();
    }
};

struct B : public A<B> {
    void func() {}
};
1 голос
/ 02 ноября 2013

Во-первых, я думаю, что код, близкий к правильному:

template<typename T> struct A {
    auto func()
     -> decltype(static_cast<T*>(this)->func()) 
    {
        return static_cast<T*>(this)->func();
    }
};
class B : public A<B> {
    void func(){
    }
};

Как указал Мотти. Однако, что все еще терпит неудачу, и я думаю по той причине, что возвращаемый тип базы должен быть известен, когда B отклонен для наследования от A<B>, но, поскольку B еще не определено , это становится проблемой курицы и яйца.

Тем не менее, это может быть наконец возможно в C++1y с помощью простого auto (без decltype), я пытался с gcc-4.8.2

template<typename T> struct A {
    auto func()
    //c++1y// -> decltype(static_cast<T*>(this)->func()) 
    {
        return static_cast<T*>(this)->func();
    }
};
class B : public A<B> {
    void func(){
    }
};

Компилирует (c++ -std=c++1y) и запускает:

int main(){
  B b; b.func();
}

Два отказа от ответственности: я не знаю, почему это работает. Я не знаю, насколько это стандартно.

...