如何使用 SFINAE 只为合适的类型启用某个 std::initializer_list 构造函数

How to use SFINAE to enable a certain std::initializer_list constructor only for suitable types

下面是我正在尝试完成的精简、可重现的示例。我的用例是自定义容器 class 模板。 class 有一个构造函数,采用 std::initializer_list 元素类型。对于容器包含 std::unique_ptr<OwnedType> 类型的元素的情况,我想启用另一个构造函数,它采用 std::initialiser_list<typename std::unique_ptr<OwnedType>::pointer> 又名 std::initializer_list<OwnedType*>。我的方法是使用 SFINAE 禁用该构造函数,以防我的容器 class 的元素类型没有名为 pointer 的类型,根据我的理解,这应该是一个有效的 SFINAE 失败案例并且没有编译错误cppreference

下列出的示例
#include <vector>
#include <memory>

template <typename T>
struct MyVector
{
    /** General initializer list constructor */
    MyVector (std::initializer_list<T> il) : vector (il) {}

    /** Constructor for the case MyVector<std::unique_ptr<SomeType>>. 
        Should be treated as an SFINAE failure for non unique_ptr types 
    */
    template <typename OwnedTypePtr = typename T::pointer>
    MyVector (std::initializer_list<OwnedTypePtr> il)
    {
        vector.reserve (il.size());
        for (auto* ptr : il)
            vector.emplace_back (ptr);
    }

    std::vector<T> vector;
};

template <typename T>
using UniqueVector = MyVector<std::unique_ptr<T>>;

int main()
{
    UniqueVector<int> uv { new int (0), new int (1), new int (2) };
    MyVector<int> v { 0, 1, 2 };

    return 0;
}

(在 godbolt.org 上使用 clang 10.0.0 时编译失败)

我好像理解错了,因为 clang 抱怨说

type 'int' cannot be used prior to '::' because it has no members
in instantiation of template class 'MyVector<int>' requested here
    MyVector<int> v { 0, 1, 2 };

所以我期望的有效 SFINAE 失败案例被解释为编译错误。我很感兴趣为什么上面的代码不是有效的 SFINAE 构造以及如何正确执行它的解决方案。

============================================= ========================

编辑:初始问题可以通过@super 给出的答案来解决。不幸的是,现在在我的现实世界场景中,我 运行 遇到了 std::complex 值向量的问题,这些向量由标量值初始化,在这些更改之前有效。调整后的例子

#include <vector>
#include <memory>
#include <complex>

template <typename T>
struct MyVector
{
    /** General initializer list constructor */
    MyVector (std::initializer_list<T> il) : vector (il) {}

    /** Constructor for the case MyVector<std::unique_ptr<SomeType>>. 
        Should be treated as an SFINAE failure for non unique_ptr types 
    */
    template <typename U = T, typename OwnedTypePtr = typename U::pointer>
    MyVector (std::initializer_list<OwnedTypePtr> il)
    {
        vector.reserve (il.size());
        for (auto* ptr : il)
            vector.emplace_back (ptr);
    }

    std::vector<T> vector;
};

template <typename T>
using UniqueVector = MyVector<std::unique_ptr<T>>;

int main()
{
    UniqueVector<int> uv { new int (0), new int (1), new int (2) };
    MyVector<std::complex<int>> v { 0, 1, 2 };

    return 0;
}

live demo on godbolt

我不明白为什么编译器在这里选择新的唯一 ptr 重载,据我所知,std::complex 没有名为 pointer.

的 public 类型

您只漏了一个细节。

SFINAE 仅适用于直接上下文,不包括 class 中的模板参数。只有方法中的模板参数。

解决这个问题的一种方法是在方法中使用额外的模板参数,默认为 T

template <typename U = T, typename OwnedTypePtr = typename U::pointer>
MyVector (std::initializer_list<typename U::pointer> il)
{
    vector.reserve (il.size());
    for (auto* ptr : il)
        vector.emplace_back (ptr);
}

当我们以无效的方式使用 U 时,SFINAE 将在此处启动。