C++ 如何在具有相同签名的两个构造函数之间有条件地切换?
给定一个具有模板参数C++ 如何在具有相同签名的两个构造函数之间有条件地切换?,c++,templates,c++14,variadic-templates,typetraits,C++,Templates,C++14,Variadic Templates,Typetraits,给定一个具有模板参数typename T和class Tuple的类,如果Tuple具有std::vector类似的成员函数reserve和push_back,我想提供一个特殊的构造函数。如果Tuple没有这样的成员函数,那么我想提供一个特殊的构造函数,如果Tuple可以从类型可转换为T的可变参数构造,即 template<typename T, class Tuple> class vector { template<typename... Elements,
typename T
和class Tuple
的类,如果Tuple
具有std::vector
类似的成员函数reserve
和push_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:假设第一个构造函数不存在,为什么以下代码会导致编译器错误“无法从初始值设定项列表转换为向量”
:
模板
类元组
{
公众:
元组(){}
元组(std::初始值设定项\列表){}
};
int main()
{
向量x={1,2,3};
返回0;
}
假设我们有两种类型特征:
template <class T, Tuple> struct satisfies_A;
template <class T, Tuple> struct satisfies_B;
模板结构满足_A;
模板结构满足_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&&... );
};
模板结构标记{};
模板
类向量
{
结构A_标记{};
结构B_标记{};
struct na_tag{};//是否要支持回退?
公众:
模板
向量(标签、元素和…);
模板
向量(标签、元素和…);
};
这些构造函数根据两种不同的类型特征执行您希望它们执行的任何操作。现在,我们可以介绍如下类型:
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
使用ctor\u tag=std::conditional\u t<
满足A::值,
A_标签,
std::有条件的<
满足_B::值,
B_标签,
na_标签>>;//或者只是空虚
并酌情转发:
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)...)
{ }
模板::值>,
class=std::如果>
向量(元素和元素,元素和…元素)
:vector(标记{},std::forward(元素),std::forward(元素)…)
{ }
差不多吧 当模板和构造函数参数既不是向量也不是元组时,我不清楚您想要什么行为。假设您只是希望这两个构造函数不合格,并且在尝试调用它们时不希望出现一些默认/回退行为(即,您将提供其他将被调用的构造函数),下面是解决您的特定问题的一个尝试:
#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*/ }
};
#包括
#包括
名称空间详细信息{
模板
结构void\u t\u helper{using type=void;};
模板
使用void\u t=typename void\u t\u helper::type;
模板
自动是矢量(int,T&T)
->脱模(
t、 向后推(std::declval()),
void(t.reserve(std::declval()),
std::true_type()
);
模板
std::false_类型是_矢量型(long,T&);
模板<
typename T,typename元素,typename…参数,
typename=void\u t
>
自动类似于tuplelike(int)
->decltype(void(T{std::declval()…}),std::true_type());
模板
std::false_类型为_元组(long);
}
模板
使用is_vectorlike=decltype(detail::is_vectorlike(0,std::declval());
模板
使用is_tuplelike=decltype(细节::is_tuplelike(0));
// ...
模板
结构向量
{
向量()=默认值;
模板>
显式向量(元素和e、元素和…e)
:矢量(
是像向量的{},
是_tuplelike{},
标准:正向(e),
标准::转发。。。
)
{ }
私人:
模板
向量(std::true\u类型、\u、元素和…)
{/*类向量*/}
模板
向量(std::false\u类型、std::true\u类型、元素和…)
{/*类元组*/}
};
注意:在我看来,对于类似向量的情况,您似乎希望确保所有
元素
都可以转换为T
,但因为您没有这样说,所以这没有。;-] 当两个条件(A
和B
)都不满足时会发生什么情况。然后,ctor\u-tag
将成为no\u-tag
。是否需要为该标记提供第三个构造函数?@0xbadf00d这取决于不满足是否是逻辑错误。如果不实现no_标记大小写,则在任何一个前提条件(实作类型的参数)未满足时,都会出现编译器错误。这可能非常有用。@richardhoges如果Tuple
合适,可以有条件地启用这些构造函数。如果Tuple
不合适,则应禁用这些构造函数。不应生成编译器错误,因为我不想强制Tuple
满足这两个条件之一。在这种情况下,是的,您需要一个接受No_标记的构造函数also@RichardHodges但我不想提供这样的构造函数。如果Tuple
满足两个条件之一,则形式vector::vector(Elements&&…Elements)
的构造函数应该只存在。否则,此构造函数不应存在。那么,我应该如何处理no_标记
-constructor?
#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*/ }
};