2016-03-04 97 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,这也是因为你没有这么说,但这不是。 ; - ]

相关问题