你如何特化模板类中的成员函数?

问题描述

假设我有以下课程:

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;
};

是否有更好的方法来做到这一点,而不涉及将此函数填充到虚拟类/结构中并传递我的 this 指针?

PS:实际上,这与指针无关,而是与不同类型的容器有关。指针只是这里使用的一个简单示例。

解决方法

您可以避免编写任何特化,并使用 std::is_pointerif 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 替代品来使用此模式。>

,

如果您有权访问 ,则可以使用 concepts and constraints 来清除对任何 SFINAE、专业化或 if constexpr 的需求。这只是允许您使用不同的实例化标准定义相同的函数 N 次,这在 IMO 中更具可读性。

这与 SFINAE 方法几乎相同,但不需要糟糕的语法(没有 std::declvaldecltype 等)。它也不要求所有实现都存在于一个函数定义中,例如 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;
};

Live Example

在这种方法中有 3 个不同的条目:

  • 所有模板的基本后备
  • 适用于任何指针类型的实现,以及
  • 适用于可转换为 T 的任何 SomeType 类型的实现
,

使用 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) 方法的类型并当且仅当 {{1} } 是支持 T 方法的类型的指针

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...