如何检查类型 'T' 是否具有 'T(std::initializer_list<U>)' 构造函数
how to check if type 'T' has 'T(std::initializer_list<U>)' constructor
我有一个函数func <T> (...)
,它必须分成两个分支;
第一个分支:类型 T
具有 T(std::initializer_list<U>)
构造函数的情况。
第二个分支:类型 T
没有 T(std::initializer_list<U>)
构造函数的情况。
我目前的实现如下:
template<typename T, typename U>
using has_init_list_ctor = std::enable_if_t<std::is_constructible_v<
T,
std::initializer_list<U>
>>;
// version for T with initialization list ctor
template<
typename T,
typename = std::enable_if_t<
std::is_detected_v<has_init_list_ctor, T, /* idk how to auto-deduce type U */>
>
>
void func() {
//...
}
// version for T without initialization list ctor
template<
typename T,
typename = std::enable_if_t<
!std::is_detected_v<has_init_list_ctor, T, /* idk how to auto-deduce type U */>
>
>
void func() {
//...
}
但它有一个缺陷。我不知道如何从 T
.
自动推断类型 U
理想的解决方案是:
template<typename T>
struct deduce_U_from_T
{
// implementation.
usign type = /* ??? */;
};
template<typename T>
using has_init_list_ctor = std::enable_if_t<std::is_constructible_v<
T,
std::initializer_list<
typename deduce_U_from_T<T>::type
>
>>;
但我不知道如何实施 deduce_U_from_T
。
有什么办法可以解决这个问题吗?
或者有什么解决方法吗?
更新:
函数 func <T> (...)
是对 std::alocator_traits::construct()
的模仿。
我正在尝试实现我自己的 "allocator" 以使用 std::vector
和智能指针。一般情况下,我会使用默认的std::alocator_traits
,但是这次,我需要从"special"池中申请内存(这是我实现的东西,可以调用"virtual heap",它通过 T * get_memory <T> (...)
等方法访问,池在 mem 分配期间执行额外的操作,并提供不同的 "modes" 分配 - 我很抱歉非常通用,但目前它是 WIP,并且它不断变化)
func <T> (...)
(allocator_traits::construct()
)
的简单实现
template<typename T>
class allocator_traits
{
//...
public:
template<typename... Args>
static
std::enable_if_t<
std::is_detected_v<has_init_list_ctor, T>,
void
> construct(T * ptr, Args && ... args)
{
new(ptr) T(std::forward<Args>(args)...); // normal brackets // construct with placment-new
}
template<typename... Args>
static
std::enable_if_t<
!std::is_detected_v<has_init_list_ctor, T>,
void
> construct(T * ptr, Args && ... args)
{
new(ptr) T{ std::forward<Args>(args)... }; // curly brackets // construct with placment-new
}
//...
};
区别在于可以用大括号构造类型 T
(当类型 T
没有 T(std::initializer_list<U>)
构造函数时。
Is there any way to solve this problem?
是的。不要解决它。你不应该试图猜测用户想要什么样的初始化。只需这样做:
new (ptr) T(std::forward<Args>(args)...)
如果用户想要使用 initializer_list
构造函数,他们可以传入 initializer_list
的实例,这样就可以正常工作。
更有趣的情况是聚合,这就是为什么它们在 C++20 中可以用括号初始化(参见 P0960)。但这可以通过传入一个具有适当转换运算符的参数来解决。也就是说,如果我想构造一个:
struct X { int i; };
并使用括号使其工作,我可以传入一个类型的参数:
struct X_factory { int i; operator X() const { return X{i}; } };
并且在保证复制省略的情况下,我们无论如何都能得到正确的效果。
无论如何,initializer_list
实际上与问题没有严格的关系。您可能想要的(我不建议这样做)是:
if constexpr (std::is_constructible_v<T, Args...>) {
new (ptr) T(std::forward<Args>(args)...);
} else {
new (ptr) T{std::forward<Args>(args)...};
}
或者可能以相反的顺序为 direct-list-initializable 写一个 trait。
@Barry 是对的,这是个坏主意。
操作方法如下:
#include <initializer_list>
#include <utility>
#include <iostream>
struct any_il{
template<class U>
operator std::initializer_list<U>()const{ return {}; }
};
struct foo {
foo(std::initializer_list<int>){}
foo(foo&&)=default;
};
template<class T, class=void>
struct can_from_il:std::false_type{};
template<class T>
struct can_from_il<T, decltype(void( T(any_il{}) ) )>:std::true_type{};
int main(){
std::cout << can_from_il<foo>{}() << can_from_il<int>{}() <<"\n";
}
它有很多缺陷。
我有一个函数func <T> (...)
,它必须分成两个分支;
第一个分支:类型
T
具有T(std::initializer_list<U>)
构造函数的情况。第二个分支:类型
T
没有T(std::initializer_list<U>)
构造函数的情况。
我目前的实现如下:
template<typename T, typename U>
using has_init_list_ctor = std::enable_if_t<std::is_constructible_v<
T,
std::initializer_list<U>
>>;
// version for T with initialization list ctor
template<
typename T,
typename = std::enable_if_t<
std::is_detected_v<has_init_list_ctor, T, /* idk how to auto-deduce type U */>
>
>
void func() {
//...
}
// version for T without initialization list ctor
template<
typename T,
typename = std::enable_if_t<
!std::is_detected_v<has_init_list_ctor, T, /* idk how to auto-deduce type U */>
>
>
void func() {
//...
}
但它有一个缺陷。我不知道如何从 T
.
U
理想的解决方案是:
template<typename T>
struct deduce_U_from_T
{
// implementation.
usign type = /* ??? */;
};
template<typename T>
using has_init_list_ctor = std::enable_if_t<std::is_constructible_v<
T,
std::initializer_list<
typename deduce_U_from_T<T>::type
>
>>;
但我不知道如何实施 deduce_U_from_T
。
有什么办法可以解决这个问题吗? 或者有什么解决方法吗?
更新:
函数 func <T> (...)
是对 std::alocator_traits::construct()
的模仿。
我正在尝试实现我自己的 "allocator" 以使用 std::vector
和智能指针。一般情况下,我会使用默认的std::alocator_traits
,但是这次,我需要从"special"池中申请内存(这是我实现的东西,可以调用"virtual heap",它通过 T * get_memory <T> (...)
等方法访问,池在 mem 分配期间执行额外的操作,并提供不同的 "modes" 分配 - 我很抱歉非常通用,但目前它是 WIP,并且它不断变化)
func <T> (...)
(allocator_traits::construct()
)
template<typename T>
class allocator_traits
{
//...
public:
template<typename... Args>
static
std::enable_if_t<
std::is_detected_v<has_init_list_ctor, T>,
void
> construct(T * ptr, Args && ... args)
{
new(ptr) T(std::forward<Args>(args)...); // normal brackets // construct with placment-new
}
template<typename... Args>
static
std::enable_if_t<
!std::is_detected_v<has_init_list_ctor, T>,
void
> construct(T * ptr, Args && ... args)
{
new(ptr) T{ std::forward<Args>(args)... }; // curly brackets // construct with placment-new
}
//...
};
区别在于可以用大括号构造类型 T
(当类型 T
没有 T(std::initializer_list<U>)
构造函数时。
Is there any way to solve this problem?
是的。不要解决它。你不应该试图猜测用户想要什么样的初始化。只需这样做:
new (ptr) T(std::forward<Args>(args)...)
如果用户想要使用 initializer_list
构造函数,他们可以传入 initializer_list
的实例,这样就可以正常工作。
更有趣的情况是聚合,这就是为什么它们在 C++20 中可以用括号初始化(参见 P0960)。但这可以通过传入一个具有适当转换运算符的参数来解决。也就是说,如果我想构造一个:
struct X { int i; };
并使用括号使其工作,我可以传入一个类型的参数:
struct X_factory { int i; operator X() const { return X{i}; } };
并且在保证复制省略的情况下,我们无论如何都能得到正确的效果。
无论如何,initializer_list
实际上与问题没有严格的关系。您可能想要的(我不建议这样做)是:
if constexpr (std::is_constructible_v<T, Args...>) {
new (ptr) T(std::forward<Args>(args)...);
} else {
new (ptr) T{std::forward<Args>(args)...};
}
或者可能以相反的顺序为 direct-list-initializable 写一个 trait。
@Barry 是对的,这是个坏主意。
操作方法如下:
#include <initializer_list>
#include <utility>
#include <iostream>
struct any_il{
template<class U>
operator std::initializer_list<U>()const{ return {}; }
};
struct foo {
foo(std::initializer_list<int>){}
foo(foo&&)=default;
};
template<class T, class=void>
struct can_from_il:std::false_type{};
template<class T>
struct can_from_il<T, decltype(void( T(any_il{}) ) )>:std::true_type{};
int main(){
std::cout << can_from_il<foo>{}() << can_from_il<int>{}() <<"\n";
}
它有很多缺陷。