使用 SFINAE 禁用模板 class 成员函数
Disabling a template class member function with SFINAE
假设我有一个 class 接受某种类型 T
。这意味着它可以接受某种类型 optional<U>
。我想禁用一个函数,如果它不是 optional
类型,但如果它是......那么我想知道那个类型 U
.
我已经能够通过模板禁用该功能,但我不知道如何处理检测模板化模板 class 而不编写相同的 class 两次并制作一个模板化模板版本。
Code:
class Dummy{};
template <typename T>
class C {
T t;
public:
C(T t) : t(std::move(t)) { }
T get() {
return t;
}
// Will clearly fail when T doesn't have a value_type
template <typename R = T, typename OptT = typename T::value_type, typename = std::enable_if_t<std::is_same_v<T, optional<OptT>>>>
std::vector<OptT> stuff() {
std::vector<OptT> vec;
// Do stuff, fill vec
return vec;
}
};
int main() {
C<Dummy> c{Dummy()}; // Error
// C<optional<Dummy>> c{Dummy()}; // Works fine
c.get();
}
在这样做的过程中,我得到了
main.cpp: In instantiation of 'class C':
main.cpp:33:14: required from here
main.cpp:25:23: error: no type named 'value_type' in 'class Dummy'
std::vector<OptT> stuff() {
^~~~~
注意:如果这个class有专门化也没关系,我只关心检测它是否std::optional
。我不需要它为任何其他类型工作......只是可选的。这可能允许某种模板专业化,但我在研究它时没有弄清楚如何做到这一点。
如何让这个函数只在类型为std::optional
的时候出现,然后当是那个类型的时候,能够抓取optional里面的类型?我可以在不触及 T
的模板定义的情况下执行此操作吗? (例如,我可以在不将其更改为 template <template <typename> T>
或必须复制此 class 的情况下将其保留为 template <typename T>
来完成上述两项操作)
我建议定义一个自定义类型特征如下
template <typename>
struct optionalType
{ };
template <typename T>
struct optionalType<std::optional<T>>
{ using type = T; };
定义 type
(std::optional<T>
的 T
类型)当且仅当使用 std::optional
.
调用时
现在你的 stuff()
变成了
template <typename R = T,
typename OptT = typename optionalType<R>::type>
std::vector<OptT> stuff() {
std::vector<OptT> vec;
// Do stuff, fill vec
return vec;
}
观察 std::optional
的类型 OptT
仅在 R
(又名 T
)是 std::optional
时出现;你有一个替换失败,否则,禁用 stuff()
.
你可以验证
C<Dummy> c0{Dummy()};
C<std::optional<Dummy>> c1{Dummy()};
//c0.stuff(); // compilation error
c1.stuff(); // compile
您的问题:
// Will clearly fail when T doesn't have a value_type
template <typename R = T,
typename OptT = typename T::value_type,
typename = std::enable_if_t<std::is_same_v<T, optional<OptT>>>>
您是否引入了一个新的虚拟模板参数 R
,但您仍在使用旧参数 T
进行所有检查。所以 none 的支票实际上是依赖的。将它们交换为 R
就可以了。
另一种方法是仅使用标记参数来延迟到不同的函数:
template <typename> struct tag { };
template <typename R=T>
auto stuff() -> decltype(stuff_impl(tag<R>{})) {
return stuff_impl(tag<R>{});
}
现在你可以有效地使用普通模板推导来提取类型:
template <typename U>
std::vector<U> stuff_impl(tag<std::optional<U>>) {
return {};
}
假设我有一个 class 接受某种类型 T
。这意味着它可以接受某种类型 optional<U>
。我想禁用一个函数,如果它不是 optional
类型,但如果它是......那么我想知道那个类型 U
.
我已经能够通过模板禁用该功能,但我不知道如何处理检测模板化模板 class 而不编写相同的 class 两次并制作一个模板化模板版本。
Code:
class Dummy{};
template <typename T>
class C {
T t;
public:
C(T t) : t(std::move(t)) { }
T get() {
return t;
}
// Will clearly fail when T doesn't have a value_type
template <typename R = T, typename OptT = typename T::value_type, typename = std::enable_if_t<std::is_same_v<T, optional<OptT>>>>
std::vector<OptT> stuff() {
std::vector<OptT> vec;
// Do stuff, fill vec
return vec;
}
};
int main() {
C<Dummy> c{Dummy()}; // Error
// C<optional<Dummy>> c{Dummy()}; // Works fine
c.get();
}
在这样做的过程中,我得到了
main.cpp: In instantiation of 'class C':
main.cpp:33:14: required from here
main.cpp:25:23: error: no type named 'value_type' in 'class Dummy'
std::vector<OptT> stuff() { ^~~~~
注意:如果这个class有专门化也没关系,我只关心检测它是否std::optional
。我不需要它为任何其他类型工作......只是可选的。这可能允许某种模板专业化,但我在研究它时没有弄清楚如何做到这一点。
如何让这个函数只在类型为std::optional
的时候出现,然后当是那个类型的时候,能够抓取optional里面的类型?我可以在不触及 T
的模板定义的情况下执行此操作吗? (例如,我可以在不将其更改为 template <template <typename> T>
或必须复制此 class 的情况下将其保留为 template <typename T>
来完成上述两项操作)
我建议定义一个自定义类型特征如下
template <typename>
struct optionalType
{ };
template <typename T>
struct optionalType<std::optional<T>>
{ using type = T; };
定义 type
(std::optional<T>
的 T
类型)当且仅当使用 std::optional
.
现在你的 stuff()
变成了
template <typename R = T,
typename OptT = typename optionalType<R>::type>
std::vector<OptT> stuff() {
std::vector<OptT> vec;
// Do stuff, fill vec
return vec;
}
观察 std::optional
的类型 OptT
仅在 R
(又名 T
)是 std::optional
时出现;你有一个替换失败,否则,禁用 stuff()
.
你可以验证
C<Dummy> c0{Dummy()};
C<std::optional<Dummy>> c1{Dummy()};
//c0.stuff(); // compilation error
c1.stuff(); // compile
您的问题:
// Will clearly fail when T doesn't have a value_type
template <typename R = T,
typename OptT = typename T::value_type,
typename = std::enable_if_t<std::is_same_v<T, optional<OptT>>>>
您是否引入了一个新的虚拟模板参数 R
,但您仍在使用旧参数 T
进行所有检查。所以 none 的支票实际上是依赖的。将它们交换为 R
就可以了。
另一种方法是仅使用标记参数来延迟到不同的函数:
template <typename> struct tag { };
template <typename R=T>
auto stuff() -> decltype(stuff_impl(tag<R>{})) {
return stuff_impl(tag<R>{});
}
现在你可以有效地使用普通模板推导来提取类型:
template <typename U>
std::vector<U> stuff_impl(tag<std::optional<U>>) {
return {};
}