2017-02-11 43 views
0

我工作的一類,以確定紋理內的圖像,像這樣:定義一個類,其成員函數存在根據模板基類

using level_t = ... 
using layer_t = ... 
using face_t = ... 

template <bool> 
struct levels {}; 

template <> 
struct levels<true> { 
    level_t level = level_t{0}; 
}; 

template <bool> 
struct layers {}; 

template <> 
struct layers<true> { 
    layer_t layer = layer_t{0}; 
}; 

template <bool> 
struct faces {}; 

template <> 
struct faces<true> { 
    face_t face = face_t{0}; 
}; 

template <bool HasLevels, bool HasLayers, bool HasFaces> 
struct index : levels<HasLevels>, layers<HasLayers>, faces<HasFaces> {}; 

level_tlayer_tface_t是int的一種「strong typedef」(基本上不同的類型看起來像一個int,但沒有隱式轉換)。

現在,我可以用我喜歡的類型,像這樣:

using Index1 = index<true, true, false>; 
void f(Index1); 

Index1 v; 
v.level = level_t{1}; 
v.layer = layer_t[1}; 
f(v); 

但是,爲了使生活爲我的用戶更容易我希望讓這樣的代碼:

f({level_t{1}, layer_t{1}}; 

的集合初始化是出應有的基類,所以我需要自己寫一個構造函數;編寫一個支持所有參數組合的構造函數(但不是重新排序)的最好/最聰明的方法是什麼?

+0

是否有一個原因,你必須使用多繼承而不是聚合? – zneak

+0

「所有參數組合(但不包括重新排序)」是什麼意思?您是否指向任何模板參數值而不重新構造函數的參數? –

+0

@zneak在這種情況下是的,根據'index'類型是如何配置的,一些屬性必須消失並嘗試使用它們必須導致編譯錯誤 – dvd

回答

1

如果您傳遞錯誤的類型或數量的參數,錯誤消息有點可怕,所以我不確定這是否值得,&hellip;

有一件事需要的是過濾和隨機訪問一組類型的方法;現實我會用這個BrigandBoost.Hana,但我會用std::tuple<>這裏堅持把這個標準:

namespace detail { 
    template<template<bool> class Holder, bool B> 
    std::integral_constant<bool, B> holder_value_(Holder<B>); 

    template<typename HolderT> 
    constexpr decltype(detail::holder_value_(std::declval<HolderT>())) holder_value() { 
     return {}; 
    } 

    template<typename UnfilteredT, typename FilteredT> 
    struct filter_holders; 

    template<typename UnfilteredT, typename FilteredT = std::tuple<>> 
    using filter_holders_t = typename filter_holders<UnfilteredT, FilteredT>::type; 

    template<typename... Fs> 
    struct filter_holders<std::tuple<>, std::tuple<Fs...>> { 
     using type = std::tuple<Fs...>; 
    }; 

    template<typename U, typename... Us, typename... Fs> 
    struct filter_holders<std::tuple<U, Us...>, std::tuple<Fs...>> : filter_holders< 
     std::tuple<Us...>, 
     std::conditional_t<holder_value<U>(), std::tuple<Fs..., U>, std::tuple<Fs...>> 
    > { }; 
} 

需要的另一件事是一種方法來查找intlike型a「holder'型披着(例如levels<true>包裝level_t等)。這可以非侵入性地完成,但在這裏我將假定一個內部的value_type typedef。

通過這些適當的它相當簡單–我們得到了它的模板參數是true寫構造方法的初始化是正義的包的擴展基本類型的包:

namespace detail { 
    template<typename... HolderTs> 
    std::tuple<typename HolderTs::value_type...> value_types_(std::tuple<HolderTs...>); 

    template<typename HoldersT> 
    using value_types_t = decltype(detail::value_types_(std::declval<HoldersT>())); 
} 

template<bool HasLevels, bool HasLayers, bool HasFaces> 
struct index : levels<HasLevels>, layers<HasLayers>, faces<HasFaces> { 
private: 
    using bases_t = std::tuple<levels<HasLevels>, layers<HasLayers>, faces<HasFaces>>; 
    using true_bases_t = detail::filter_holders_t<bases_t>; 
    using arg_types_t = detail::value_types_t<true_bases_t>; 

    template<std::size_t... Is, typename... ArgTs> 
    index(std::index_sequence<Is...>, ArgTs&&... args) 
    : std::tuple_element_t<Is, true_bases_t>{std::forward<ArgTs>(args)}... { } 

public: 
    index() = default; 

    template< 
     typename... ArgTs, 
     std::size_t S = sizeof...(ArgTs), 
     typename = std::enable_if_t< 
      S && std::is_same<std::tuple<std::decay_t<ArgTs>...>, arg_types_t>{} 
     > 
    > 
    index(ArgTs&&... args) 
    : index{std::make_index_sequence<S>{}, std::forward<ArgTs>(args)...} { } 
}; 

Online Demo

編輯:更新更嚴格的實施,不允許convertible-b並非相同的參數類型,並且不會錯誤地干擾複製/移動構建。

+0

我已經在使用Boost.Hana,所以我正在更改你的代碼來使用它。從來沒有想過有可能使用元函數(std :: tuple_element_t)作爲基類初始化程序,哇:) – dvd

+0

@dvd:是的,基礎初始值設定器只需要以某種方式命名基地,但不一定直接;所以'decltype',簡單的typedef,特徵/元功能等都是可行的選擇。我很高興你已經在使用Hana;海事組織將很容易導致這裏最乾淨的實施,它只是一個堅實的圖書館,在沒有3頁長答案的情況下出售的SO答案。 ; - ] – ildjarn

+0

@dvd :(我不確定問題作者是否收到回覆編輯的通知......)更新了對索引的複製/移動構造函數的重要修復。 – ildjarn

0

這是允許代碼的演示代碼。

#include <iostream> 
#include <type_traits> 

struct A { 
    A() = default; 
    A(const A &) { 
    std::cout << "A" << std::endl; 
    } 
}; 

struct B { 
    B() = default; 
    B(const B &) { 
    std::cout << "B" << std::endl; 
    } 
}; 

struct C { 
    C() = default; 
    C(const C &) { 
    std::cout << "C" << std::endl; 
    } 
}; 

template <class... Base> 
struct Hyper : public Base... { 
    template <class... Args> 
    Hyper(Args&&... args) : Base(std::forward<Args>(args))... {} 
}; 

template <class... Base> 
auto factory(Base&&... args) { 
    return Hyper<std::decay_t<Base>...>(std::forward<Base>(args)...); 
} 

int main() { 
    auto obj = factory(A{}, B{}, C{}); 
    return 0; 
} 

Hyper的所有基類都是複製/移動構造的。

一些static_assert可以添加到factory,以防止它產生任何怪異的類。

正如評論所說,這不是一個好主意。如果你做得對,一切都會好起來的,但是如果你做錯了,即使是一個小錯誤,模板+多重繼承也可能是你的噩夢。 (最終可能會出現100000行編譯器錯誤消息。)

+0

爲了防止時髦的類,你可以使用is_valid_base = std :: integral_constant :: value來定義一個類型特徵'template || std :: is_same :: value || std :: is_same :: value>;' –

相關問題