如何使用 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;
}
我不明白为什么编译器在这里选择新的唯一 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 将在此处启动。
下面是我正在尝试完成的精简、可重现的示例。我的用例是自定义容器 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;
}
我不明白为什么编译器在这里选择新的唯一 ptr 重载,据我所知,std::complex
没有名为 pointer
.
您只漏了一个细节。
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 将在此处启动。