имитировать создание экземпляра шаблона функции C ++ с неявным преобразованием - PullRequest
1 голос
/ 18 ноября 2009

Я уже задавал два вопроса , связанных с тем, что я пытаюсь сделать (один решен, один из которых я скоро закрою). Я знаю, что создание экземпляра шаблона C ++ не допускает никаких неявных преобразований (см., Например, этот комментарий ), но я хотел бы смоделировать его.

Предположим, у меня есть следующий код скелета:

template <class T>
struct Base_A{
    virtual void interface_func() const = 0;
};
template <class T>
struct Derived_A : public Base_A<T>{
    typedef T value_type;
    void interface_func() const{}
};

template <class T>
struct Base_B{
    virtual void interface_func() = 0; // note: non-const
};
template <class T>
struct Derived_B : public Base_B<T>{
    typedef T value_type;
    void interface_func(){}
};

template <class BType>
struct Adapter : public Base_A<typename BType::value_type>{
    BType &ref_B;
    Adapter(BType &inst_B):ref_B(B_inst){}
    void interface_func() const{} // does stuff with ref_B to simulate an A
};

template <class Should_Always_Be_Base_A>
void f(const Should_Always_Be_Base_A &arg){
    // Only Base_A can be passed in by const ref
    // Passing in a Base_B by const ref would not work.
}

Derived_A<int> A;
Derived_B<int> B;
f(A); // passes in A by const ref
f(B); // I want to pass in Adapter<Derived_B<int> >(B)

Я хочу, чтобы параметр шаблона для функции f всегда был производным классом Base_A или Adapter. Ответ на ограничение типа arg может быть сделан , но неявное преобразование в адаптер не может. Есть какой-либо способ сделать это? В результате я хочу назвать f как f(A) или f(B), и в обоих случаях мне нужно знать фактический производный тип A или B в пределах f (f не может просто увидеть ссылка на базовый класс).

За исключением:

В настоящее время у меня просто работает f(A), а f(B) фактически вызывает перегрузку, которая выполняет конструирование адаптера, но у меня есть другие функции, которые принимают N аргументов, каждый из которых может быть A или B, поэтому мне нужно 2 ^ N перегрузок.

Для любопытных, это в приложении к матричной библиотеке, над которой я работаю. Base_A представляет тип базовой матрицы, а Base_B представляет тип базовой матрицы. Для операций, которые изменят аргумент матрицы, мне нужно передать матрицу по неконстантной ссылке или модифицируемое представление матрицы по const-ref. Адаптер - это всего лишь тривиальный адаптер матрицы к представлению. Так, например, у меня в настоящее время есть функция, такая как

Scale(const MatrixViewBase<T> &Mview, const T &scale_factor){
    // does the actual work
}
Scale(MatrixBase<T> &M, const T &scale_factor){
    Scale(Adapter<MatrixBase<T> >(M), scale_factor);
}

Утомительно и подвержено ошибкам делать 2 ^ N копий всех этих функций просто для создания необходимых перегрузок для обработки как представлений, так и не представлений. Как такового, этого недостаточно, поскольку я хочу, чтобы Scale мог знать полный производный тип Mview, а не только базовый класс, потому что я буду потенциально генерировать экземпляры типов, зависящих от Mview.

Редактировать 1: Изменены все типы B, чтобы иметь неконстантные функции интерфейса. Это было первоначальное намерение, поэтому извиняюсь за любую путаницу.

Редактировать 2: Иметь этот рабочий код, все еще требующий 2 ^ N перегрузок, но я могу с этим смириться, если кто-то не предложит, как с этим бороться.

#include <iostream>

template <class T>
struct ReadableMatrix{
    typedef T value_type;
};
template <class T>
struct WritableMatrix{
    typedef T value_type;
};
template <class T>
struct WritableMatrixView{
    typedef T value_type;
};

template <class T>
struct Matrix : public WritableMatrix<T>{
    typedef T value_type;
    typedef ReadableMatrix<T> readable_matrix;
    typedef WritableMatrix<T> writable_matrix;
};

template <class T>
struct MatrixView : public WritableMatrixView<T>{
    typedef T value_type;
    typedef ReadableMatrix<T> readable_matrix; // not really used; needs an adapter before using
    typedef WritableMatrixView<T> writable_matrix;
};

template <class T, class R>
struct IsReadableMatrix{
};
template <class T, class R>
struct IsReadableMatrix<ReadableMatrix<T>, R>{
    typedef R type;
};

template <class T, class R>
struct IsWritableMatrix{
};
template <class T, class R>
struct IsWritableMatrix<WritableMatrix<T>, R>{
    typedef R type;
};

template <class T, class R>
struct IsWritableMatrixView{
};
template <class T, class R>
struct IsWritableMatrixView<WritableMatrixView<T>, R>{
    typedef R type;
};

template <class TA, class TB>
    typename IsReadableMatrix<typename TA::readable_matrix,
    typename IsWritableMatrixView<typename TB::writable_matrix,
void
    >::type>::type
Copy(const TA &A, const TB &B){
    std::cout << "Here" << std::endl;
}

template <class TA, class TB>
    typename IsReadableMatrix<typename TA::readable_matrix,
    typename IsWritableMatrix<typename TB::writable_matrix,
void
    >::type>::type
Copy(const TA &A, TB &B){
    std::cout << "Here2" << std::endl;
}

int main(){
    Matrix<int> M, M2;
    MatrixView<int> V, V2;
    Copy(M, M2);
    Copy(V, V2);
    Copy(M, V);
    Copy(V, M);
}

Ответы [ 4 ]

1 голос
/ 18 ноября 2009

Вам нужны базовые классы для шаблонов? Можете ли вы внедрить даже «более базовый» не шаблонный класс? Если да, вы можете сделать что-то подобное:

struct Base_A{
    virtual void interface_func() const = 0;
};

template <class T>
struct Derived_A : public Base_A{
    typedef T value_type;
    void interface_func() const{}
};

struct Base_B{
    virtual void interface_func() const = 0;
};

template <class T>
struct Derived_B : public Base_B{
    typedef T value_type;
    void interface_func() const{}
};

struct Adapter
{
    const Base_A* pA;
    const Base_B* pB;

    Adapter(const Base_B &inst_B) : pB(&inst_B), pA(0){}
    Adapter(const Base_A &inst_A) : pA(&inst_A), pB(0){}
    void interface_func() const
    {
        if( 0 != pA )
            pA->interface_func();
        else if( 0 != pB )
            pB->interface_func();
    }
};


void f(const Adapter &arg)
{
    arg.interface_func(); // will call proper interface_func
}


int main()
{
    Derived_A<int> A;
    Derived_B<int> B;
    f(A);
    f(B);
}
1 голос
/ 18 ноября 2009

Попробуйте следующее:

template <template <typename> View, typename T>
struct Adapter
{
    // Leave empty to cause errors if used, or you could
    // provide a generic adapter for View<typename T::value_type>
}

// Partial specialization for a given base.
template <typename T>
struct Adapter<MatrixView, T> : MatrixView<typename T::value_type>
{
    const T& t;

    Adapter (const T& t)
      : t(t)
    {}

    void some_virtual()
    {
      // Do stuff
    }
}

template <template <typename> View, typename T>
const View<T>& adapt (const View<T>& v)
{
  return v;
}

template <template <typename> View, typename T>
View<T> adapt (const T& t)
{
  return Adapter<View, T>(t);
}

template <typename T, typename U>
foo(const MatrixViewBase<T> &Mview, const MatrixViewBase<U> &Mview2);

template <typename T, typename U>
foo (const T& t, const U& u)
{
  return foo(adapt<MatrixViewBase>(t), adapt<MatrixViewBase>(u));
}

Я положил const везде, где мог; Вам не нужно использовать его в любой ситуации, если это не подходит. Вы можете использовать специализации Adapter для данного T для дальнейшей адаптации поведения.

Интересно, что я впервые рекомендовал параметры шаблона шаблона.

0 голосов
/ 18 ноября 2009

Я должен был сделать это некоторое время назад, и самое простое (для поддержки) решение, которое я придумал, состояло в том, чтобы иметь только один класс (Matrix), который знает, владеет ли он своими собственными данными или является представлением чужих данных. Например.,

Matrix A(n,m);  //Creates a new matrix of size (n,m)
Matrix B=View(A);  //Creates a matrix that's a view into A
Matrix C=View(pData,n,m); //Creates a matrix thats a view of data in a pointer that someone else owns

Foo(A);
Foo(B);
Foo(C);

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

Отредактировано, чтобы добавить:

Почему бы просто не позволить BLAS управлять транспонированием для вас? В конце концов, любая из функций BLAS просто захочет получить указатель на непрерывный кусок памяти. Так что, пока ваш класс Matrix знает, как добраться до этой памяти, вы хороши. Если класс Matrix имеет собственный флаг isTransposed внутри, вы можете сделать что-то вроде этого:

Matrix A(n,m);
Matrix B=TransposedView(pData,m,n);
Matrix C=A*B;

Matrix& operator*(const Matrix& lhs, const Matrix& rhs)
{
  Matrix result(lhs.Rows(),rhs.Cols()); //or did i get that backwards :)
  char TransL=lhs.IsTransposed()?'T':'N';
  char TransR=rhs.IsTransposed()?'T':'N';
  _dgemm(TransL, TransR, ..., lhs.Data(), ..., rhs.Data(), ..., result.Data());
  return result
}
0 голосов
/ 18 ноября 2009

Я не изучал детали шаблонного решения, но пока вы работаете с ним, вы можете проверить библиотеку препроцессора boost, чтобы помочь вам в определении 2 ^ N вариантов шаблона. Это не будет хорошо во время компиляции, но будет лучше, чем создание вариантов вручную.

...