簡體   English   中英

如何在具有相同簽名的兩個構造函數之間進行有條件的切換?

[英]How can I conditionally switch between two constructors with same signature?

給定一個帶有模板參數typename Tclass Tuple我想提供一個特殊的構造函數,如果Tuplestd::vector -like成員函數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;
}

假設我們有兩種類型特征:

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)...)
{ }

這樣的事情。

當模板和構造函數參數既不像矢量也不像元組時,我不清楚你想要什么樣的行為。 假設您只是希望這兩個構造函數不合格,並且在嘗試調用它們時不希望有某些默認/回退行為(即,您將提供將被調用的其他構造函數),這里是嘗試解決您的特定問題:

#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來表示類似矢量的情況,但是既然你沒有這么說,那就沒有了。 ; - ]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM