【问题标题】:How do you pass a template name to a method?如何将模板名称传递给方法?
【发布时间】:2017-04-20 07:14:31
【问题描述】:

假设我有两个用于迭代容器的模板函数:

template <typename I, typename C>
It Prev(I i, const C& c) noexcept {
    Expects(i != c.end());
    if (i == c.begin()) return c.end();
    return i - 1;
}

template <typename I, typename C>
It Next(I i, const C& c) noexcept {
    Expects(i != c.end());
    return i + 1;
}

还有一个使用这些模板的具有 GetNextElement 和 GetPrevElement 的类:

struct MyClass {
  std::vector<int> elements;

  int* GetNextElement(std::vector<int>::iterator i) {
    auto next = Next(i, elements);
    if (next == elements.end()) return nullptr;
    return &*it;
  }
  int* GetPrevElement(std::vector<int>::iterator i) {
    auto prev = Prev(i, elements);
    if (prev == elements.end()) return nullptr;
    return &*it;
  }
};

这两个方法做同样的事情,除了调用不同的模板。如何将这些转换为单个成员函数,可以像

一样调用
MyClass mc;
// ...
auto it = mc.elements.begin();
auto next = mc.GetElement<Next>(it);
// Or maybe mc.GetElement(it, Next);

【问题讨论】:

  • NextPrev 期望引用与迭代器关联的容器。 GetElement 不知道该容器。由于您似乎在使用std::vector::iterators,我不知道如何实现这一点。您要么需要将容器传递给GetElement,要么从NextPrev 中删除该要求。
  • 您还没有定义Expects 是什么。您使用It 作为PrevNext 的返回类型类型,但它是未声明的。看起来您打算使用 I 类型。
  • 我可能在我的示例中简化了太多并且暴露了调用者不知道的事情。调用者不知道元素是如何存储在类中的。他只知道他可以得到一个元素的标识符,并且假设他可以调用“GetNextElement”或“GetPrevElement”。由于这两种方法几乎完全相同,我希望能够有一个方法并让调用者指定它是否应该使用 Next 或 Prev 模板函数来获取所需的元素。

标签: c++ templates


【解决方案1】:
#define OINVOKE(...) __VA_ARGS__(decltype(args)(args)...)
#define OVERLOADS_OF(...) [](auto&&...args)\
    noexcept(noexcept(OINVOKE(__VA_ARGS__)))\
    ->decltype(OINVOKE(__VA_ARGS__))\
    { return OINVOKE(__VA_ARGS__); }

auto fNext=OVERLOADS_OF(Next);
auto fPrev=OVERLOADS_OF(Prev);

然后:

template<auto* pf>
int* GetElement(std::vector<int>::iterator i) {
  auto next = (*pf)(i, elements);
  if (next == elements.end()) return nullptr;
  return &*it;
}
auto it = mc.elements.begin();
auto next = GetElement<&fNext>(it);

需要 C++17。

【讨论】:

    【解决方案2】:

    如果你可以将你的函数包装在struct中,这个问题就会大大简化。

    struct Prev
    {
        template <typename I, typename C>
        I operator()(I i, const C& c) noexcept {
            Expects(i != c.end());
            if (i == c.begin()) return c.end();
            return i - 1;
        }
    };
    
    struct Next
    {
        template <typename I, typename C>
        I operator()(I i, const C& c) noexcept {
            Expects(i != c.end());
            return i + 1;
        }
    };
    

    然后您可以简单地将它们作为模板参数传递。

    struct MyClass 
    {
    public:
        auto begin() { return elements.begin(); }
        auto end() { return elements.end(); }
    
        template<typename T, typename I>
        I GetElement(I iter)
        {
            return T()(iter, this->elements);
        }
    
    private:
        std::vector<int> elements;
    };
    
    int main()
    {
        MyClass mc;
        auto it = mc.begin();
        auto next = mc.GetElement<Next>(it);
        auto prev = mc.GetElement<Prev>(it);
    }
    

    【讨论】:

    • 调用者将无法访问 mc.elements 本身(它是私有的),因此免费函数无济于事。我只是为了简化示例而将其公开。我希望调用者能够告诉方法使用 Next 或 Prev,但不知道使用什么类型来实例化它。
    • 在您的示例中,GetElement&lt;Next&gt; 无法知道要使用 mcmc.element 的哪个实例。你想让GetElement&lt;T&gt; 成为MyClass 的成员方法吗?
    • 啊,我刚刚注意到了。是的,GetElement 旨在成为成员,替换两个 GetPrevElement 和 GetNextElement 方法。我修正了错字。对不起。所以编辑并没有真正解决问题,因为它调用了两个成员而不是替换它们。
    • @Kian 再次编辑。
    【解决方案3】:

    你可以试试这个代码:

    template <std::vector<int>::iterator (*FOO)(std::vector<int>::iterator, const std::vector<int>&)>
    std::vector<int>::iterator GetElement(std::vector<int>::iterator i)
    {
         return FOO(i, elements);
    }
    

    【讨论】:

      猜你喜欢
      • 2011-06-14
      • 2021-06-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多