2010-07-20 2 views
0

Я пытаюсь понять шаблон, используемый в ublas. картина такова:C++ код наследования + CRTP

struct vector : vector_expression<vector> 

где vector_expression как это:

template<class E> 
class vector_expression { 
... 
// no constructor or E pointer/reference in class 
// 
const E &operator()() const { 
     return *static_cast<const E*>(this); 
} 

полный исходный код здесь: http://www.tena-sda.org/doc/5.2.2/boost/dd/d44/vector__expression_8hpp-source.html#l00088

мой вопрос, как же *static_cast<const E*>(this) работу? он полагается на наследование?

следующий вопрос: если я получить

template<class E> 
class vector_expression2 : private vector_expression<E> 
{ 
    //friend class ublas::vector_expression<E>; // this is the fix 
    typedef vector_expression<E> base; 
    const E& operator()() const { return base::operator()(); } 
}; 

я получаю ошибку компилятора относительно недоступного vector_expression базы в статическом гипсе. почему это происходит?

Спасибо

+0

извините, я испортил оригинальное сообщение и сделал разъяснения – Anycorn

ответ

2

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

template<typename V, typename S> 
some_type operator*(V v, S s); // vector * scalar 

template<typename V, typename S> 
some_type operator*(S s, V v); // scalar * vector 

, но это не будет работать потому что обе декларации по существу эквивалентны, и никто не сказал, что V предполагается векторным выражением, а S предполагается скалярным выражением. Итак, что разработчики uBlas сделали это использовать CRTP, чтобы ограничить эти шаблоны:

template<typename V, typename S> 
some_Type operator*(vector_expression<V> ve, scalar_expression<S> se); 

Чтобы сделать эту работу все скалярные выражения S должны извлечь из scalar_expression<S> и все векторные выражения V должны извлечь из vector_expression<V>. Таким образом, этот оператор рассматривается только в том случае, если первый операнд действительно является выражением для вектора, а второй аргумент действительно является выражением для скаляра. Вы можете перегрузить этот шаблон функции вторым, который меняет оба параметра, и все в порядке.

Теперь, чтобы иметь возможность получить доступ к чему-либо из V и S (производным типам), нам нужно сделать отливку из базового класса в производный класс. Это и есть оператор преобразования в базовом классе.Поскольку базовый класс знает производный класс (это параметр шаблона), это не проблема. Имеет смысл выбрать самый слабый оператор-оператор, который позволяет этому актеру избегать ошибок. Это static_cast. Его можно использовать для преобразования базы * в производную * без каких-либо значительных накладных расходов.

Я не понимаю, что вы пытаетесь сделать с вашим кодом

template<class E> 
class vector_expression2 : private vector_expression<E>; 

Если вы хотите, чтобы написать свой собственный вектор экспрессии, в качестве шаблона вы могли бы сделать это следующим образом:

template<class E> 
class my_parameterized_vector_expression 
: public vector_expression<my_parameterized_vector_expression<E> >; 

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

+0

спасибо. Я создаю мост для cublas, и мне нужно предотвратить преобразование в выражения ublas, в то же время повторно используя их фреймворк. Так что я использую наследование для контроля доступа – Anycorn

1

Ошибка доступности, что вы имеете в виду не имеет смысла. vector_expression - struct, а вызов функции operator() является общедоступным. Возможно, вы пытались вызвать vector_expression2::operator()? Это дало бы вам ошибку, потому что вы определили этот оператор как закрытый.

Решение проблемы может быть даже проще, чем вы думаете. Если вы смотрите через ublas исходный код, который вы бы увидели, что любые классы, вытекающие vector_expression выдать себя в качестве аргумента шаблона:

template<class M> 
class matrix_row: public vector_expression<matrix_row<M> > { 
}; 

Что это означает, что vector_expression может отбрасывать параметр шаблона для себя, так как параметр шаблона происходит от vector_expression , следовательно, *static_cast<const E*>(this) работает, что является просто причудливым способом сказать *((const E*)this).

Попробуйте переписать vector_expression2 класс следующим образом:

template<class E> 
class vector_expression2 : public vector_expression<vector_expression2<E>> 
{ 
}; 
+0

, так что ... это что-то вроде очень простой динамической броски, реализованной с использованием шаблонов? Каково название этого шаблона? – Anycorn

+1

Все действие, получаемое от класса, которое принимает производный класс в качестве параметра шаблона, - это CRTP - Curiously Recurring Template Pattern. Этот конкретный оператор можно рассматривать как скомпилированное безопасное понижение к производному типу, для которого вам в противном случае понадобилось бы 'dynamic_cast' с RTTI. –