SFINAE 和参数数量
SFINAE and number of parameters
我有一个模板 class C<T>
,我打算用 T
作为其他一些 classes A
和 B
实例化. C<T>
有一个方法 foo
,我希望其签名取决于 T
是实例化为 A
还是 B
。例如,考虑以下代码:
#include <iostream>
#include <string>
class A {
public:
void message() {
std::cout << "message with no args" << std::endl;
}
};
class B {
public:
void message(int x) {
std::cout << "message with " << x << std::endl;
}
};
template<typename T>
class C {
private:
T internal;
public:
C(T& x) {
internal = x;
}
void call() {
internal.message();
}
void call(int x) {
internal.message(x);
}
};
int main(int argc, char* argv[]) {
A a;
B b;
C<A> ca(a);
C<B> cb(b);
ca.call();
cb.call(42);
// ca.call(42); ERROR HERE
return 0;
}
这运行正确。 ca.call(42)
会引发编译错误,因为没有方法 A::message(int)
。但是,如果我出于某种原因在 A
中引入方法 A::message(int)
,代码可能允许调用 ca.call(42)
,我想阻止这种情况。
我知道 SFINAE 技术将允许声明一个方法 C::call(T::call_type x)
,其中 T::call_type
将是 T
的每个预期实例的类型定义。但是,这只允许我更改 C::call
的参数类型。相反,我想在 T
上制作 的签名 (特别是参数数量)。因此,即使 A
中有方法 A::message(int)
,我也会阻止 ca.call(42)
成为有效调用。
有什么办法吗?
您可以使用模板特化来做到这一点:
// Main template used by every other type T.
template<typename T>
class C; // or some other implementation.
// This gets used for T = A.
template<>
class C<A> {
private:
A internal;
public:
C(A& x) {
internal = x;
}
void call() {
internal.message();
}
};
// This gets used for T = B.
template<>
class C<B> {
private:
B internal;
public:
C(B& x) {
internal = x;
}
void call(int x) {
internal.message(x);
}
};
如果您不喜欢,您需要复制一些通用代码,那么您可以拥有一个包含所有这些通用内容的基础 class,并在每个专业化中继承它。
我不知道SFINAE的所有来龙去脉,但是你觉得这个怎么样?
template <
typename = std::enable_if_t<std::is_same<std::decay_t<T>, A>::value>>
void call() {
internal.message();
}
template <
typename = std::enable_if_t<std::is_same<std::decay_t<T>, B>::value>>
void call(int x) {
internal.message(x);
}
你也可以用== false
template <
typename = std::enable_if_t<std::is_same<std::decay_t<T>, B>::value == false>>
我有一个模板 class C<T>
,我打算用 T
作为其他一些 classes A
和 B
实例化. C<T>
有一个方法 foo
,我希望其签名取决于 T
是实例化为 A
还是 B
。例如,考虑以下代码:
#include <iostream>
#include <string>
class A {
public:
void message() {
std::cout << "message with no args" << std::endl;
}
};
class B {
public:
void message(int x) {
std::cout << "message with " << x << std::endl;
}
};
template<typename T>
class C {
private:
T internal;
public:
C(T& x) {
internal = x;
}
void call() {
internal.message();
}
void call(int x) {
internal.message(x);
}
};
int main(int argc, char* argv[]) {
A a;
B b;
C<A> ca(a);
C<B> cb(b);
ca.call();
cb.call(42);
// ca.call(42); ERROR HERE
return 0;
}
这运行正确。 ca.call(42)
会引发编译错误,因为没有方法 A::message(int)
。但是,如果我出于某种原因在 A
中引入方法 A::message(int)
,代码可能允许调用 ca.call(42)
,我想阻止这种情况。
我知道 SFINAE 技术将允许声明一个方法 C::call(T::call_type x)
,其中 T::call_type
将是 T
的每个预期实例的类型定义。但是,这只允许我更改 C::call
的参数类型。相反,我想在 T
上制作 的签名 (特别是参数数量)。因此,即使 A
中有方法 A::message(int)
,我也会阻止 ca.call(42)
成为有效调用。
有什么办法吗?
您可以使用模板特化来做到这一点:
// Main template used by every other type T.
template<typename T>
class C; // or some other implementation.
// This gets used for T = A.
template<>
class C<A> {
private:
A internal;
public:
C(A& x) {
internal = x;
}
void call() {
internal.message();
}
};
// This gets used for T = B.
template<>
class C<B> {
private:
B internal;
public:
C(B& x) {
internal = x;
}
void call(int x) {
internal.message(x);
}
};
如果您不喜欢,您需要复制一些通用代码,那么您可以拥有一个包含所有这些通用内容的基础 class,并在每个专业化中继承它。
我不知道SFINAE的所有来龙去脉,但是你觉得这个怎么样?
template <
typename = std::enable_if_t<std::is_same<std::decay_t<T>, A>::value>>
void call() {
internal.message();
}
template <
typename = std::enable_if_t<std::is_same<std::decay_t<T>, B>::value>>
void call(int x) {
internal.message(x);
}
你也可以用== false
template <
typename = std::enable_if_t<std::is_same<std::decay_t<T>, B>::value == false>>