2016-03-04 93 views
1

給定一個帶模板參數的類typename Tclass Tuple我想提供一個特殊的構造函數,如果Tuple具有std::vector類似成員函數reservepush_back。如果Tuple有沒有這樣的成員函數,那麼我想提供一個特殊的構造,如果Tuple是從類型轉換爲T可變參數構造的,即我怎樣纔能有條件地切換兩個具有相同簽名的構造函數?

template<typename T, class Tuple> 
class vector 
{ 
    template<typename... Elements, 
     typename = decltype(std::declval<Tuple>().push_back(T())), 
     typename = decltype(std::declval<Tuple>().reserve(size_type()))> 
    vector(Elements&&... elements) 
    { /* ... */ } 

    template<typename... Elements, typename = typename = decltype(Tuple{ static_cast<T>(std::declval<Elements>())... })> 
    vector(Elements&&... elements) 
    { /* ... */ } 
}; 

問題1:顯然,在代碼上面的編譯器不知道我想盡可能採用第一個構造函數。無論如何,我怎樣才能達到理想的行爲?

問題2:假設第一個構造函數不存在,爲什麼下面的代碼會導致編譯器錯誤「不能從初始化列表轉換爲vector<double, Tuple<double>>」:

template<typename T> 
class Tuple 
{ 
public: 
    Tuple() { } 
    Tuple(std::initializer_list<T>) { } 
}; 

int main() 
{ 
    vector<double, Tuple<double>> x = { 1, 2, 3 }; 
    return 0; 
} 

回答

2

比方說,我們有兩種類型的特徵:

template <class T, Tuple> struct satisfies_A; 
template <class T, Tuple> struct satisfies_B; 

而且我們想提供一個構造函數來基於滿意度來調用一個或另一個。我們可以先加直接構造函數分別與一些額外的標籤類型:

template <class> struct tag{}; 

template <typename T, class Tuple> 
class vector 
{ 
    struct A_tag { }; 
    struct B_tag { }; 
    struct na_tag { }; // if you want to support a fallback? 

public: 
    template <class U=T, class UTuple=Tuple, class... Elements, 
     class = std::enable_if_t<satsfies_A<U,UTuple>::value>> 
    vector(tag<A_tag>, Elements&&...); 

    template <class U=T, class UTuple=Tuple, class... Elements, 
     class = std::enable_if_t<satsfies_B<U,UTuple>::value>> 
    vector(tag<B_tag>, Elements&&...); 
}; 

這些構造函數做什麼是你希望他們基於這兩種不同類型的特點做。現在,我們可以引入一個類型,如:

using ctor_tag = std::conditional_t< 
    satisfies_A<T, Tuple>::value, 
    A_tag, 
    std::conditional_t< 
     satisfies_B<T, Tuple>::value, 
     B_tag, 
     na_tag>>; // or just void 

和前進酌情:

template <class Element, class... Elements, 
    class = std::enable_if_t<!is_template<tag, std::decay_t<Element>>::value>, 
    class = std::enable_if_t<std::is_constructible<vector, tag<ctor_tag>, Element&&, Elements&&...>::value>> 
vector(Element&& element, Elements&&... elements) 
: vector(tag<ctor_tag>{}, std::forward<Element>(element), std::forward<Elements>(elements)...) 
{ } 

類似的東西。

+0

當兩個條件('A'和'B')都不滿足時發生了什麼。然後,'ctor_tag'將是'no_tag'。我是否需要爲該標籤提供第三個構造函數? – 0xbadf00d

+0

@ 0xbadf00d它取決於不滿意是否是邏輯錯誤。如果不符合前提條件之一(實現類型的參數),則不執行no_tag情況會爲您提供編譯器錯誤。這可能非常有用。 –

+0

@RichardHodges如果'Tuple'適合,我們的想法是有條件地啓用這些構造函數。如果'Tuple'不適合,那麼這些構造函數應該被禁用。不應該生成編譯器錯誤,因爲我不想強制'Tuple'滿足兩個條件之一。 – 0xbadf00d

1

當模板和構造函數參數既不是向量狀,也不是元組狀時,我不清楚你想要什麼樣的行爲。假設你只是希望這兩個構造函數不符合條件,並且在嘗試調用它們時不想要一些默認/回退行爲(即,您將提供其他將被調用的構造函數),下面試圖解決您的特定問題:

#include <type_traits> 
#include <utility> 

namespace detail { 

template<typename...> 
struct void_t_helper { using type = void; }; 

template<typename... Ts> 
using void_t = typename void_t_helper<Ts...>::type; 

template<typename ElemT, typename T, typename SizeT = typename T::size_type> 
auto is_vectorlike(int, T& t) 
-> decltype(
     t.push_back(std::declval<ElemT>()), 
     void(t.reserve(std::declval<SizeT>())), 
     std::true_type() 
    ); 

template<typename, typename T> 
std::false_type is_vectorlike(long, T&); 

template< 
    typename T, typename ElemT, typename... ArgTs, 
    typename = void_t<decltype(static_cast<ElemT>(std::declval<ArgTs>()))...> 
> 
auto is_tuplelike(int) 
-> decltype(void(T{ std::declval<ArgTs>()... }), std::true_type()); 

template<typename...> 
std::false_type is_tuplelike(long); 

} 

template<typename T, typename ElemT> 
using is_vectorlike = decltype(detail::is_vectorlike<ElemT>(0, std::declval<T&>())); 

template<typename T, typename ElemT, typename... ArgTs> 
using is_tuplelike = decltype(detail::is_tuplelike<T, ElemT, ArgTs...>(0)); 

// ... 

template<typename T, typename Tuple> 
struct vector 
{ 
    vector() = default; 

    template<typename Element, typename... Elements, typename = std::enable_if_t< 
     is_vectorlike<Tuple, T>{} || is_tuplelike<Tuple, T, Element&&, Elements&&...>{} 
    >> 
    explicit vector(Element&& e, Elements&&... es) 
     : vector(
      is_vectorlike<Tuple, T>{}, 
      is_tuplelike<Tuple, T, Element&&, Elements&&...>{}, 
      std::forward<Element>(e), 
      std::forward<Elements>(es)... 
     ) 
    { } 

private: 
    template<typename _, typename... Elements> 
    vector(std::true_type, _, Elements&&...) 
    { /*vector-like*/ } 

    template<typename... Elements> 
    vector(std::false_type, std::true_type, Elements&&...) 
    { /*tuple-like*/ } 
}; 

Nb在我看來,你想要確保所有的Elements都可以轉換爲T,這也是因爲你沒有這麼說,但這不是。 ; - ]

相關問題