2015-03-19 147 views
2

我想實現一個模板類,該模板類既可用作派生基礎,又可用作具體類,如果模板參數是正確的。C++類模板或純虛擬

我想要實現的是,如果模板類的方法不能被實例化,但派生類提供了一個實現,這是行。 但是,如果模板可以完全實例化,那麼這個類自己就是有效的。

例子:

// interface class 
class A 
{ 
public: 
    virtual void foo() = 0; 
    virtual ~A() {} 
}; 

// template class 
template <typename T> 
class B : public A 
{ 
public: 
    /* if this tenplate can be instantiated */ 
    foo() 
    { 
     T obj; 
     std::cout << obj; 
    } 
    /* else 
    foo() = 0; 
    */ 
}; 

// concrete classes 
// will work on it's own 
typedef B<std::string> C; 

class D : public B<void> 
{ 
    // B<void>::foo won't instantiate on it's own 
    // so we provide help here 
    foo() {} 
}; 

int main(int argc, char const *argv[]) 
{ 
    A * = new C(); // all good 
    A * = new D(); // error: cannot instantiate B<void>::foo 
    return 0; 
} 

有沒有辦法實現這樣的效果呢?

+1

你可能對sfinae有一些好運..看看std :: enable_if(或boost :: enable_if) – Pete 2015-03-19 12:25:02

+0

另一種選擇是提供專業化的B Pete 2015-03-19 12:25:59

回答

3

使用SFINAE,你可以這樣做:

namespace detail 
{ 
    // an helper for traits 
    template <typename T> 
    decltype(T{}, std::cout << T{}, std::true_type{}) 
    helper_has_default_constructor_and_foo(int); 

    template <typename T> 
    std::false_type helper_has_default_constructor_and_foo(...); 

    // the traits 
    template <typename T> 
    using has_default_constructor_and_foo = decltype(helper_has_default_constructor_and_foo<T>(0)); 

    // genaral case (so when traits is false) 
    template <typename T, typename = has_default_constructor_and_foo<T>> 
    struct C : public A {}; 

    // specialization when traits is true 
    template <typename T> 
    struct C<T, std::true_type> : public A 
    { 
     void foo() override { std::cout << T{}; } 
    }; 

} 

最後:

template <typename T> 
class B : public detail::C<T> 
{ 
}; 

live demo

0

你可以專門爲B<void>

// template class 
template <typename T> 
class B : public A 
{ 
public: 
    virtual void foo() 
    { 
     T obj; 
     std::cout << obj; 
    } 
}; 
template <> 
class B<void> : public A 
{ 
public: 
    virtual void foo() = 0; 
}; 
0

必須專門B<>而不能使用SFINAE(成員foo上)。 SFINAE僅適用於模板,但成員函數模板不能爲virtual

有可以實現專業化不同的方式,但最直接的方式是簡單而明確的

template<typename T> 
class B : public A 
{ 
    /* ... */    // not overridden foo() by default 
}; 

template<> 
class B<WhatEver> : public A 
{ 
    virtual foo(); 
};