2017-02-16 66 views
0

我一直在試驗一個我稱之爲PolymorphicVariant的類,它非常類似於unique_ptr,不同之處在於它存儲的是內聯值,而不是分配給堆。爲此,我使用了variant類型(自制版本)和一些靜態斷言來檢查所有變體是否實現了給定的接口(即它們都來自相同的基類)。然後我有方法得到一個指向基類的指針。然後,我可以將PolymorphicVariant存儲在容器中,並且它的行爲方式與存儲指向基類的指針相同,除非沒有間接指向。多態變體,並將一個類型的引用綁定到另一個類型的引用

template <typename Base, typename... Derivees> 
class PolymorphicVariant { 
    static_assert(std::is_polymorphic<Base>{}, 
        "Base type has to be polymorphic"); 

    static_assert(meta::And<std::is_base_of<Base, Derivees>...>{}, 
        "Derivees have to derive from Base"); 

    using VariantType = Variant<Derivees...>; 
    using Types = meta::List<Derivees...>; 

    struct ToBasePtrVisitor { 
     template <typename T> 
     auto operator()(const T& value) const -> const Base* { 
      return &value; 
     } 
    }; 

public: 
    // ... Constructors and assignment operators ... 

    auto get() const -> const Base* { 
     return apply(ToBasePtrVisitor{}, m_storage); 
    } 

private: 
    VariantType m_storage; 
}; 

然後我就可以做這樣的東西:

// Bar and Baz both implement IFoo 
using Foo = PolymorphicVariant<IFoo, Bar, Baz>; 

std::vector<Foo> foos; 

for (int i = 0; i < 10; ++i) { 
    if (i % 2 == 0) { 
     foos.emplace_back(Bar{}); 
    } else { 
     foos.emplace_back(Baz{}); 
    } 
} 

for (const auto& elem : foos) { 
    elem.get()->some_virtual_fn(); 
} 

所以現在我的問題。比方說,我有一個函數,它的PolymorphicVariant的引用(就像使用一個函數採用指針抽象基類):

auto mutate_foo(Foo& foo) -> void { 
    foo.get()->mutating_function(); 
} 

Bar my_bar; 
mutate_foo(my_bar); // This doesn't work 

這不工作的原因是我不能綁定參考Bar參考Foo(即PolymorphicVariant<IFoo, Bar, Baz>)。有什麼辦法可以做到這一點嗎?某種方式告訴編譯器Bar參考實際上可以被視爲Foo參考?無需更改IFooBar的實施。如果這是不可能的,我不得不將mutate_foo的簽名更改爲mutate_foo(IFoo* foo) -> void,但是這種感覺不那麼幹淨,因爲我希望Foo能夠像首選類型那樣用來表示「包含滿足要求的東西接口IFoo「。謝謝!

+1

通用接口是'Base'。 'Foo'不是'Bar',因此當函數需要Foo時傳遞一個Bar並不合適。 –

+0

爲什麼不給這個變體隱式地轉換爲基類型,並讓'mutate_foo'也引用基類型? –

+0

我同意Richard的觀點:你可以改變'Foo&foo'來存儲'Baz',而不是提供'Bar my_bar'的調用者。 – dyp

回答

0

這裏:

auto mutate_foo(Foo& foo) -> void { 
    foo.get()->mutating_function(); 
} 

創建一個類型FooRef

auto mutate_foo(FooRef foo) -> void { 
    foo.get()->mutating_function(); 
} 

FooRef基於PolymorphicAnyRefPolymorphicAnyRef與您的PolymorphicVariant非常相似,但是它將指針存儲爲來自Base的事物以及從該指針獲取Base*的方式。

一個簡單的實現是簡單地存儲一個Base*並讓get()返回它。

template<class Base> 
struct PolymorphicAnyRef { 
    Base* ptr = 0; 
    Base* get() const { return ptr; } 
    // blah, etc 

    template<class T> 
    PolymorphicAnyRef(T& t): ptr(std::addressof(t)) {} 
    template<class...Ts> 
    PolymorphicAnyRef(PolymorphicVariant<Base, Ts...>& pv): 
    ptr(pv.get()) 
    {} 
}; 

這種過度的,因爲它源自-從任何Base或任何引用給PolymorphicVariant<Base,???>轉換採取Base*增加了一些語法糖。

順便說一句,而不是PolymorphicVariant,我會寫這是保證存儲任何的Ts...一個PolymorphicAny<Base, Ts...>,但還可以存儲任何比小少排列Ts...是足夠的「常規」(可複製,無論你需要什麼)。派生檢查是在構造中而不是在類型實例化時完成的。

它可能然後存儲一個函數指針void*轉換爲存儲內部AnyBase*,並將其用於get()。編寫完整的super_any支持添加新類型的擦除方法,可讓您將該操作符與默認值一起存儲。

但我有點瘋狂。

0

有什麼辦法可以做到這一點嗎?無需更改IFooBar的實施。

我想不出有什麼辦法。我會編輯我的帖子,如果我能想到一個(好的)。


我想Foo表現得像首選類型用來指「包含的東西,滿足接口IFoo的事情」。

您可以使用template功能:

template <typename TFoo> 
auto mutate_foo(TFoo& foo) -> void { 
    foo.get()->mutating_function(); 
} 

你可能想用它檢查是否OT不TFoo實際上是實現你想要的界面謂詞來限制它:

template <typename TFoo, 
      typename std::enable_if_t<implements_foo_interface<TFoo>{}>> 
auto mutate_foo(TFoo& foo) -> void { 
    foo.get()->mutating_function(); 
} 

implements_foo_interface可以使用detection idiom輕鬆實現。

0

繼評論之後,我想到沒有必要使FooBar從通用接口繼承。如果您在FooBar的名稱空間中提供了自由函數超載,則變體訪問者已經提供了查找正確方法的管道。

例如(使用boost::variant,因爲我沒有訪問到您的圖書館):

#include <boost/variant.hpp> 
#include <memory> 
#include <iostream> 

struct Foo { 
    void do_something() { std::cout << "something\n"; } 
}; 


struct Bar { 
    // note - dissimilar interfaces 
    void do_something_else() { std::cout << "something else\n"; } 
}; 


void mutating_function(Foo& on) { 
    // overload explicit actions here 
    on.do_something(); 
} 

void mutating_function(Bar& on) { 
    // overload explicit actions here 
    on.do_something_else(); 
} 

struct invoke_mutating_function 
{ 
    template<class Target> 
    void operator()(Target& on) const { 
     mutating_function(on); 
    } 
}; 


using FooBar = boost::variant<Foo, Bar>; 

void mutating_function(FooBar& on) 
{ 
    boost::apply_visitor(invoke_mutating_function(), on); 
} 

int main() 
{ 
    FooBar x = Foo(); 
    FooBar y = Bar(); 

    mutating_function(x); 
    mutating_function(y); 

} 

預期輸出:

something 
something else 
相關問題