c++ - 如何检查类型 'T' 是否有 'T(std::initializer_list<U>)' 构造函数

标签 c++ templates constructor c++17 sfinae

我有一个函数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>) 构造函数时。

最佳答案

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)...};
}

或者可能以相反的顺序编写直接列表初始化的特征。

关于c++ - 如何检查类型 'T' 是否有 'T(std::initializer_list<U>)' 构造函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57453579/

相关文章:

python - 如何在没有元类的情况下将不同的参数传递给 __new__ 与 __init__

c++ - 如何使用默认参数从构造函数调用 super 构造函数?

c++ - C++ 中 float_max + 1 是如何定义的?

C++程序内存不足

c++ - 初始化空的2D vector ?

c++ - 视频文件中的 QVideoFrames 列表

c++ - 为什么 std::is_same 不起作用?

Java:找不到符号(构造函数)

c++ - 如何制作模板结构 vector

class - PyBind11多种类型的模板类