您如何在模板 class 中专门化成员函数?
How do you specialize a member function inside a template class?
假设我有以下 class:
template <typename T>
class SomeClass : Parent<T>
{
public:
// I have a function such as this one:
T DoSomething(const T &t)
{
return t.DoSomething(some_data);
}
// But `T` might be a pointer, so sometimes I will need something like the following
// instead (which obviously doesn't work as given):
T DoSomething(const T &t)
{
return new T(t->DoSomething(some_data));
}
private:
XYZ some_data;
};
我试图使用模板专业化以任何可能的半好的方式实现它,但陷入了一大堆模板错误中。
最后我想到了这个非常丑陋的解决方案:
template <typename T>
class SomeClass : Parent<T>
{
public:
T DoSomething(const T &x)
{
return Specializer<T>::Do(this, x);
}
private:
template <typename V>
struct Specializer {
static V Do(SomeClass *me, const V &x)
{
return x.DoSomething(me->some_data);
}
};
template <typename V>
struct Specializer<V*> {
static V* Do(SomeClass *me, const V *&x)
{
return new V(x->DoSomething(me->some_data));
}
};
XYZ some_data;
};
有没有更好的方法来做到这一点,而不涉及将此函数填充到虚拟 class/struct 并绕过我的 this
指针?
PS:实际上,这与指针无关,而是与不同类型的容器有关。指针只是此处使用的一个简单示例。
使用 SFINAE 怎么样?
例如
#include <utility>
#include <iostream>
template <typename>
struct Parent
{ };
using XYZ = int;
template <typename T>
class SomeClass : Parent<T>
{
public:
template <typename U = T>
auto DoSomething (T const & t)
-> decltype( std::declval<U>().DoSomething(std::declval<XYZ>()) )
{ std::cout << "ref\n"; return t.DoSomething(some_data); }
template <typename U = T>
auto DoSomething (T const & t)
-> std::remove_reference_t<
decltype( std::declval<U>()->DoSomething(std::declval<XYZ>()),
std::declval<T>() )>
{
using V = std::remove_reference_t<decltype(*t)>;
std::cout << "pnt\n"; return new V(t->DoSomething(some_data));
}
private:
XYZ some_data;
};
struct foo
{
foo (foo*) {}
foo () {}
foo DoSomething (int) const { return {}; }
} ;
int main()
{
SomeClass<foo> sc1;
SomeClass<foo*> sc2;
foo f;
sc1.DoSomething(f);
sc2.DoSomething(&f);
}
我的意思是:当且仅当 T
是一种支持 DoSomething(XYZ)
方法的类型时启用第一个版本,当且仅当 T
是支持 DoSomething(XYZ)
方法的类型的 指针 ?
您可以避免编写任何特化,并使用像 std::is_pointer 这样的类型特征和 if constexpr
来根据类型是否为指针类型来决定要执行的代码:
auto DoSomething(const T &t)
{
if constexpr (std::is_pointer_v<T>)
return new T(t->DoSomething(some_data));
else
return t.DoSomething(some_data);
}
如果您不想检查 T
是否是一个指针,而是想检查其他东西,您仍然可以通过放入 is_pointer
的合适替代品来使用此模式。
如果您有权访问 c++20, you can clean up the need for any SFINAE, specializations, or if constexpr
by using concepts and constraints。这只允许您使用不同的实例化标准定义相同的函数 N 次,这在 IMO 中更具可读性。
这与 SFINAE 方法几乎相同,但不需要糟糕的语法(没有 std::declval
、decltype
等)。它也不要求所有实现都存在于一个函数定义中,例如 if constexpr
方法;您所需要的只是具有不同 requires
子句的单独函数定义:
#include <concepts>
...
template <typename T>
class SomeClass : Parent<T>
{
public:
// Work for everything that's not specialized
void DoSomething(const T &t)
{
std::cout << "Basic overload" << std::endl;
}
// Only work for pointers
void DoSomething(const T& t) requires(std::is_pointer_v<T>)
{
std::cout << "Pointer overload" << std::endl;
}
// Only work if T is convertible to SomeType
void DoSomething(const T& t) requires(std::convertible_to<T, SomeType>)
{
std::cout << "Convertible to SomeType overload" << std::endl;
}
private:
XYZ some_data;
};
在这种方法中有 3 个不同的条目:
- 所有模板的基本回退
- 适用于任何指针类型的实现,并且
- 适用于可转换为
SomeType
的任何 T
类型的实现
假设我有以下 class:
template <typename T>
class SomeClass : Parent<T>
{
public:
// I have a function such as this one:
T DoSomething(const T &t)
{
return t.DoSomething(some_data);
}
// But `T` might be a pointer, so sometimes I will need something like the following
// instead (which obviously doesn't work as given):
T DoSomething(const T &t)
{
return new T(t->DoSomething(some_data));
}
private:
XYZ some_data;
};
我试图使用模板专业化以任何可能的半好的方式实现它,但陷入了一大堆模板错误中。
最后我想到了这个非常丑陋的解决方案:
template <typename T>
class SomeClass : Parent<T>
{
public:
T DoSomething(const T &x)
{
return Specializer<T>::Do(this, x);
}
private:
template <typename V>
struct Specializer {
static V Do(SomeClass *me, const V &x)
{
return x.DoSomething(me->some_data);
}
};
template <typename V>
struct Specializer<V*> {
static V* Do(SomeClass *me, const V *&x)
{
return new V(x->DoSomething(me->some_data));
}
};
XYZ some_data;
};
有没有更好的方法来做到这一点,而不涉及将此函数填充到虚拟 class/struct 并绕过我的 this
指针?
PS:实际上,这与指针无关,而是与不同类型的容器有关。指针只是此处使用的一个简单示例。
使用 SFINAE 怎么样?
例如
#include <utility>
#include <iostream>
template <typename>
struct Parent
{ };
using XYZ = int;
template <typename T>
class SomeClass : Parent<T>
{
public:
template <typename U = T>
auto DoSomething (T const & t)
-> decltype( std::declval<U>().DoSomething(std::declval<XYZ>()) )
{ std::cout << "ref\n"; return t.DoSomething(some_data); }
template <typename U = T>
auto DoSomething (T const & t)
-> std::remove_reference_t<
decltype( std::declval<U>()->DoSomething(std::declval<XYZ>()),
std::declval<T>() )>
{
using V = std::remove_reference_t<decltype(*t)>;
std::cout << "pnt\n"; return new V(t->DoSomething(some_data));
}
private:
XYZ some_data;
};
struct foo
{
foo (foo*) {}
foo () {}
foo DoSomething (int) const { return {}; }
} ;
int main()
{
SomeClass<foo> sc1;
SomeClass<foo*> sc2;
foo f;
sc1.DoSomething(f);
sc2.DoSomething(&f);
}
我的意思是:当且仅当 T
是一种支持 DoSomething(XYZ)
方法的类型时启用第一个版本,当且仅当 T
是支持 DoSomething(XYZ)
方法的类型的 指针 ?
您可以避免编写任何特化,并使用像 std::is_pointer 这样的类型特征和 if constexpr
来根据类型是否为指针类型来决定要执行的代码:
auto DoSomething(const T &t)
{
if constexpr (std::is_pointer_v<T>)
return new T(t->DoSomething(some_data));
else
return t.DoSomething(some_data);
}
如果您不想检查 T
是否是一个指针,而是想检查其他东西,您仍然可以通过放入 is_pointer
的合适替代品来使用此模式。
如果您有权访问 c++20, you can clean up the need for any SFINAE, specializations, or if constexpr
by using concepts and constraints。这只允许您使用不同的实例化标准定义相同的函数 N 次,这在 IMO 中更具可读性。
这与 SFINAE 方法几乎相同,但不需要糟糕的语法(没有 std::declval
、decltype
等)。它也不要求所有实现都存在于一个函数定义中,例如 if constexpr
方法;您所需要的只是具有不同 requires
子句的单独函数定义:
#include <concepts>
...
template <typename T>
class SomeClass : Parent<T>
{
public:
// Work for everything that's not specialized
void DoSomething(const T &t)
{
std::cout << "Basic overload" << std::endl;
}
// Only work for pointers
void DoSomething(const T& t) requires(std::is_pointer_v<T>)
{
std::cout << "Pointer overload" << std::endl;
}
// Only work if T is convertible to SomeType
void DoSomething(const T& t) requires(std::convertible_to<T, SomeType>)
{
std::cout << "Convertible to SomeType overload" << std::endl;
}
private:
XYZ some_data;
};
在这种方法中有 3 个不同的条目:
- 所有模板的基本回退
- 适用于任何指针类型的实现,并且
- 适用于可转换为
SomeType
的任何
T
类型的实现