[英]How can I conditionally switch between two constructors with same signature?
Given a class with template parameters typename T
and class Tuple
I want to provide a special constructor, if Tuple
has std::vector
-like member functions reserve
and push_back
. 给定一个带有模板参数
typename T
和class Tuple
我想提供一个特殊的构造函数,如果Tuple
有std::vector
-like成员函数reserve
和push_back
。 If Tuple
has no such member functions, then I want to provide a special constructor, if Tuple
is constructible from variadic arguments of types convertible to T
, ie 如果
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)
{ /* ... */ }
};
Question 1: Clearly, in the code above the compiler doesn't know that I want to take the first constructor whenever possible.
问题1:显然,在上面的代码中,编译器不知道我想尽可能采用第一个构造函数。 How can I achieve the desired behavior anyway?
我怎样才能达到理想的行为?
Question 2: Assuming that the first constructor doesn't exist, why does the following code lead to the compiler error "cannot convert from initializer list to
vector<double, Tuple<double>>
":问题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;
}
Let's say we have two type traits: 假设我们有两种类型特征:
template <class T, Tuple> struct satisfies_A;
template <class T, Tuple> struct satisfies_B;
And we want to provide a constructor to call one or the other based on satisfaction. 我们希望提供一个构造函数来根据满意度调用其中一个。 We can first add direct constructors for each case with some additional tag type:
我们可以先为每个案例添加一些额外的标记类型的直接构造函数:
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&&... );
};
Those constructors do whatever it is you want them to do based on the two different type traits. 那些构造函数根据两种不同的类型特征做任何你想要它们做的事情。 Now, we can introduce a type like:
现在,我们可以介绍一种类型:
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
And forward as appropriate: 并酌情转发:
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)...)
{ }
Something like that. 这样的事情。
It's unclear to me what behavior you want when the template and constructor arguments are neither vector-like nor tuple-like. 当模板和构造函数参数既不像矢量也不像元组时,我不清楚你想要什么样的行为。 Assuming you simply want these two constructors to be ineligible and don't want some default/fallback behavior when attempting to call them (ie, you'll provide other constructors that will be called instead), here's an attempt at solving your specific problem:
假设您只是希望这两个构造函数不合格,并且在尝试调用它们时不希望有某些默认/回退行为(即,您将提供将被调用的其他构造函数),这里是尝试解决您的特定问题:
#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 it seems to me that you'd want to make sure all Elements
are convertible to T
for the vector-like case as well, but since you didn't say so, this doesn't. Nb在我看来,你想要确保所有的
Elements
都可以转换为T
来表示类似矢量的情况,但是既然你没有这么说,那就没有了。 ;-] ; - ]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.