Смешивать typedef и CRTP?

Рассмотрим следующий пример:

#include <iostream>
#include <iostream>
#include <type_traits>

template<typename Type, template<typename> class Crtp>
class Base
{
public:
typedef int value;

// f1: OK
// Expected result: casts 4.2 to Base<Type, Crtp>::value
value f1() {return 4.2;}

// f2: NOT OK
// Expected result: casts 4.2 to Crtp<Type>::value
// But f2 does not compile: no type named 'value'
// in 'class Derived<double>'
typename Crtp<Type>::value f2() {return 4.2;}
};

template<typename Type>
class Derived : public Base<Type, Derived>
{
public:
typedef Type value;
};

int main()
{
Derived<double> a;
std::cout<<a.f1()<<std::endl;
std::cout<<a.f2()<<std::endl;
return 0;
}

Как решить эту проблему (Derived typedef неизвестен из Base учебный класс) ?

РЕДАКТИРОВАТЬ: я нашел очень простой трюк. Может кто-нибудь объяснить мне, почему следующее работает, а предыдущая версия не работает? Этот трюк в порядке со стандартным C ++ 11 или он работает из-за способа работы компилятора (здесь g ++ 4.7.1)?

#include <iostream>
#include <iostream>
#include <type_traits>

template<typename Type, template<typename> class Crtp>
class Base
{
public:
typedef int value;
value f1() {return 4.2;}
template<typename T = Crtp<Type>> typename T::value f2() {return 4.2;}
};

template<typename Type>
class Derived : public Base<Type, Derived>
{
public:
typedef Type value;
};

int main()
{
Derived<double> a;
std::cout<<a.f1()<<std::endl;
std::cout<<a.f2()<<std::endl;
return 0;
}

4

Решение

Вы должны использовать класс-оболочку (здесь это value_getter), который объявлен до того, как вы определите Base, Вы можете специализировать его прямо перед определением Derived:

template<typename T>
struct value_getter;

template<typename Type, template<typename> class Crtp>
class Base
{
public:
typedef int value;

value f1() {return 4.2;}

// in 'class Derived<double>'
typename value_getter<Crtp<Type> >::value f2() {return 4.2;}
};

template<typename Type>
class Derived;

template<typename Type>
struct value_getter<Derived<Type> > {
typedef Type value;
};

template<typename Type>
class Derived : public Base<Type, Derived>, public value_getter<Derived<Type> >
{
public:
};

Это не совсем красиво, но, по крайней мере, это работает.

3

Другие решения

Ваш трюк работает, потому что f2 теперь не создается, пока не будет фактически использован, когда класс Derived был завершен.

В вашем конкретном примере я мог бы просто порекомендовать сделать это:

#include <iostream>
#include <iostream>
#include <type_traits>

template<typename Type, template<typename> class Crtp>
class Base
{
public:
typedef int value;

value f1() {return 4.2;}

Type f2() {return 4.2;}
};

template<typename Type>
class Derived : public Base<Type, Derived>
{
public:
typedef Type value;
};

int main()
{
Derived<double> a;
std::cout<<a.f1()<<std::endl;
std::cout<<a.f2()<<std::endl;
return 0;
}

но ваш реальный код может иметь другие потребности, которые делают это непрактичным.

1

По вопросам рекламы [email protected]