Ограничения создания шаблона C ++ - PullRequest
0 голосов
/ 30 июня 2010

У меня есть метод foo в классе C, который вызывает либо foo_1, либо foo_2.Этот метод foo () должен быть определен в C, потому что foo () является чисто виртуальным в BaseClass, и я фактически должен создавать объекты типа C. Код ниже:

template <class T>
class C:public BaseClass{

  void foo() {
    if (something()) foo_1;
    else foo_2;

  }
  void foo_1() {
  ....
  }

  void foo_2() {
    ....
     T t;
     t.bar(); // requires class T to provide a method bar() 
     ....
   }
};

Теперь для большинства типов T foo_1достаточно, но для некоторых типов будет вызываться foo_2 (в зависимости от чего-либо ()).Однако компилятор настаивает на создании экземпляров foo_1 и foo_2, потому что любой из них может быть вызван.

Это накладывает бремя на Т, которое он должен обеспечить методом штанги.

Как мне сообщить компилятору следующее:

  • , если T не имеет bar (), все еще разрешить его как экземплярный тип?

Ответы [ 3 ]

1 голос
/ 30 июня 2010

вы можете использовать boost.enable_if. как то так:

#include <boost/utility/enable_if.hpp>
#include <iostream>

struct T1 {
    static const bool has_bar = true;
    void bar() { std::cout << "bar" << std::endl; }
};

struct T2 {
    static const bool has_bar = false;
};

struct BaseClass {};

template <class T>
class C: public BaseClass {
public:
    void foo() {
        do_foo<T>();
    }

    void foo_1() {
        // ....
    }

    template <class U>
    void foo_2(typename boost::enable_if_c<U::has_bar>::type* = 0) {
        // ....
        T t;
        t.bar(); // requires class T to provide a method bar() 
        // ....
    }

private:

    bool something() const { return false; }


    template <class U>
    void do_foo(typename boost::enable_if_c<U::has_bar>::type* = 0) {
        if (something()) foo_1();
        else foo_2<U>();
    }

    template <class U>
    void do_foo(typename boost::disable_if_c<U::has_bar>::type* = 0) {
        if (something()) foo_1();
        // I dunno what you want to happen if there is no T::bar()
    }
};

int main() {
    C<T1> c;
    c.foo();
}
0 голосов
/ 30 июня 2010

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

template <class T>
void call_bar(T& /*t*/)
{
}

template <>
void call_bar<Something>(Something& t)
{
    t.bar();
}

Исходный класс 'C' может быть изменен соответственно:

void foo_2() {
  ....
   T t;
   call_bar(t); // does not require T to provide bar() 
   ....
}

У этого недостатка есть то, что вы должны явно определить, какие типы T предоставляют метод bar, но это в значительной степени неизбежно, если вы не сможете определить во время компиляции все типы, которые предоставляют метод bar в их открытом интерфейсе или измените все эти типы поддержки столбцов, чтобы они имели что-то общее, что может быть определено во время компиляции.

0 голосов
/ 30 июня 2010

Вы можете создать интерфейс для foo_1 и foo_2, например:


class IFoo
{
public:
    virtual void foo_1()=0;
    virtual void foo_2()=0;
};

template <typename T> 
class C : public BaseClass, public IFoo
{ 

  void foo()
  { 
    if (something())
        foo_1(); 
    else
        foo_2(); 
   } 
};

template <typename T>
class DerivedWithBar : C<T>
{
public:
    void foo_1() { ... } 

    void foo_2()
    { 
         ... 
         T t; 
         t.bar(); // requires class T to provide a method bar()  
         ...
     }
}; 

template <typename T>
class DerivedNoBar : C<T>
{
public:
    void foo_1() { ... } 
    void foo_2() { ... } 
};
...