2010-06-30 2 views
0

У меня есть метод foo в классе C, который либо вызывает foo_1, либо foo_2. Этот метод Foo() должен быть определен в C, потому что Foo() является чисто виртуальным в BaseClass, и я на самом деле должны сделать объекты типа C. Код ниже:Ограничения на создание шаблона 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 не имеет бар(), по-прежнему позволяют это как тип инстанцировании?
+0

Я имел в виду, что foo() является чисто виртуальным в BaseClass. – user231536

+1

@ и имя его класса 'C'. Я не думаю, что он имеет в виду язык С. – stinky472

+2

Что такое 'something()'? Является ли это константой времени компиляции? –

ответ

1

Вы можете использовать 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

Можем ли мы сделать это без использования boost? – user231536

+0

См. Http://stackoverflow.com/questions/2937425/boostenable-if-class-template-method/2937522#2937522 - вам нужно templatize «do_foo» –

+0

Вы правы, я работаю над правильным решением: -). –

0

Вы можете создать интерфейс для 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() { ... } 
}; 
0

Я думаю, что самый простой способ, чтобы просто написать отдельный шаблон функции, что «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 предоставляют метод бара, но это почти неизбежно, если вы не можете что-то определить во время компиляции обо всех типах, которые предоставляют метод бара в их открытом интерфейсе или изменить все эти типы поддержки бара, чтобы они могли совместно использовать что-то общее, что можно определить во время компиляции.

+1

«Это имеет недостаток, что вы должны явно определить, какие типы T предоставляют метод бара, но это почти неизбежно, если вы не можете что-то определить во время компиляции обо всех типах, которые предоставляют метод бара в их открытом интерфейсе» вы можете заставить всех из них определить такую ​​функцию в своем публичном интерфейсе. ADL [уважения и находки] (http://www.gotw.ca/publications/mill02.htm), определенные в пространствах имен этих типов. См. Ссылку, которая объясняет «Принцип интерфейса». Если бы я пошел со свободной функцией, я бы основал ее на ADL. –

+0

@Johannes из того, что сказал OP, something() не является выражением, которое можно оценить во время выполнения. Без более контекстной информации мы должны предположить, что код, вызывающий как foo_1, так и foo_2 (тот, который ожидает бар), будет сгенерирован для всех типов. Т. Как мы используем ADL для этого случая? У вас есть функция call_bar, которая не вызывает бар в отдельном пространстве имен из того, которое вызывает панель, помещает типы со строкой в ​​другое пространство имен и зависит от ADL, чтобы вызвать правильную версию? – stinky472