我有一个函数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() {
//...
}
但是它有缺陷。我不知道如何从U
自动推导类型T
。
理想的解决方案是:
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()
的模仿。
我正在尝试使用std::vector
和智能指针来实现自己的“分配器”。通常情况下,我将使用默认的std::alocator_traits
,但是这一次,我需要从“特殊”池中请求内存(这是我实现的,可以称为“虚拟堆”,可以对其进行访问)通过T * get_memory <T> (...)
之类的方法,该池在mem分配期间执行其他操作,并提供不同的分配“模式”-很抱歉,它非常通用,但目前是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>)
构造函数时。
答案 0 :(得分:4)
有什么办法可以解决这个问题?
是的。不要解决。您不应该试图猜测用户想要哪种初始化。只需这样做:
$ cd /usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/Python/
$ make regen-importlib
$ make
如果用户想使用new (ptr) T(std::forward<Args>(args)...)
构造函数,则可以传入initializer_list
的实例,这样就可以了。
更有趣的情况是聚合,这就是为什么将在C ++ 20中使用括号将其初始化的原因(请参见P0960)。但这可以通过传入具有适当转换运算符的参数来解决。也就是说,如果我想构造一个:
initializer_list
并使用括号将其工作,我可以传递一个类型为
的参数struct X { int i; };
在保证复制保留的情况下,我们无论如何都能获得正确的效果。
在任何情况下,struct X_factory { int i; operator X() const { return X{i}; } };
实际上都不是与问题严格相关的。您可能想要的(但我不建议您这样做)是:
initializer_list
或者可能以相反的顺序写出可直接列表初始化的特征。
答案 1 :(得分:1)
@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";
}
它有很多缺陷。