簡體   English   中英

通過 function 參數制作模板 function select 類型

[英]Make template function select type by function parameter

我有一些課程,比如

class A {
 public:
  void OnStageOne() {}
  void OnStageTwo() {}
  void OnStageThree() {}
};

class B {
 public:
  void OnStageOne() {}
  void OnStageTwo() {}
  void OnStageThree() {}
};

class C {
 public:
  void OnStageOne() {}
  void OnStageTwo() {}
  void OnStageThree() {}
};

我有一些向量來存儲它們:

std::vector<A> va;
std::vector<B> vb;
std::vector<C> vc;

現在我需要一些這樣的操作:對向量的所有元素調用OnStageXXX ,所以我寫了一個模板 function,如下所示:

template <typename T, void(T::*F)()>
void ForAll(std::vector<T> vector) {
  for (auto& item : vector) {
    (item.*F)();
  }
}

它工作正常,除了我必須使用這樣的語法:

ForAll<A, &A::OnStageOne>(va);

由於可以為參數va推導出第一個模板參數,有什么辦法可以使代碼更清晰? 喜歡ForAll<OnStageOne>(va); ?

或者,如果這是一個 A/B 問題,我可以通過任何方式編寫ForAll<OnStageXXX>(va); 對向量的每個元素調用OnStageXXX

PS我嘗試使用std::for_each ,但這會使語法更長。 我在這里尋求更短的語法來做到這一點,如果有的話。

template <auto F, typename T>
void ForAll(std::vector<T>& vector) {
  for (auto& item : vector) {
    (item.*F)();
  }
}

用法:

ForAll<&A::OnStageOne>(va);

演示


中:

constexpr auto OnStageOne = [] (auto& t) -> decltype(t.OnStageOne()) {
  return t.OnStageOne();
};
constexpr auto OnStageTwo = [] (auto& t) -> decltype(t.OnStageTwo()) {
  return t.OnStageTwo();
};
constexpr auto OnStageThree = [] (auto& t) -> decltype(t.OnStageThree()) {
  return t.OnStageThree();
};

template <auto F, typename T>
    requires requires (T t) { F(t); }
void ForAll(std::vector<T>& vector) {
  for (auto& item : vector) {
    F(item);
  }
}

用法:

ForAll<OnStageOne>(va);

演示 2


/ (之前沒有基於范圍的 for 循環):

struct OnStageOne {
  template <typename T>
  void operator()(T& t) const { t.OnStageOne(); }
};

struct OnStageTwo {
  template <typename T>
  void operator()(T& t) const { t.OnStageTwo(); }
};

struct OnStageThree {
  template <typename T>
  void operator()(T& t) const { t.OnStageThree(); }
};

template <typename F, typename T>
void ForAll(std::vector<T>& vector) {
  for (auto& item : vector) {
    F{}(item);
  }
}

用法:

ForAll<OnStageOne>(va);

演示 3

這個答案從“XY 問題”的角度來解決這個問題:

或者,如果這是一個 A/B 問題,我可以通過任何方式編寫 ForAll(va); 之類的東西。 對向量的每個元素調用 OnStageXXX?

看來您的意圖是通過(未直接強制執行) OnStageXXX編號接口的成員函數,為類ABC實現 static 多態性。 而不是解決“如何通過指向成員函數的指針(靜態)多態調用”的問題,您可能想查看如何實現類的“靜態接口”。

作為第一步,您可以將它們實現為在某個標簽上參數化的成員 function 模板,例如enum class StageTag ,而不是為OnStageXXX函數使用單獨的非模板成員函數。 這將允許您使用標記而不是指向成員 function 的指針來調用forAll(...) function 來選擇要調用的成員 ZC1C425268E68385D1AB5074C17A94F14 特化。 例如:

#include <iostream>
#include <vector>

enum class StageTag { kOne, kTwo, kThree };

class A {
public:
  template <StageTag> void OnStage() const = delete;
};

template <> void A::OnStage<StageTag::kOne>() const {
  std::cout << "A OnStage kOne\n";
}

template <> void A::OnStage<StageTag::kTwo>() const {
  std::cout << "A OnStage kTwo\n";
}

template <> void A::OnStage<StageTag::kThree>() const {
  std::cout << "A OnStage kThree\n";
}

class B {
public:
  template <StageTag> void OnStage() const = delete;
};

template <> void B::OnStage<StageTag::kOne>() const {
  std::cout << "B OnStage kOne\n";
}

template <> void B::OnStage<StageTag::kTwo>() const {
  std::cout << "B OnStage kTwo\n";
}

template <> void B::OnStage<StageTag::kThree>() const {
  std::cout << "B OnStage kThree\n";
}

template <StageTag kTag, typename T> void ForAll(const std::vector<T> &vector) {
  for (auto &item : vector) {
    item.template OnStage<kTag>();
  }
}

int main() {
  const std::vector<A> va(3);
  const std::vector<B> vb(2);

  ForAll<StageTag::kThree>(va);
  // A OnStage kThree
  // A OnStage kThree
  // A OnStage kThree

  ForAll<StageTag::kOne>(vb);
  // B OnStage kOne
  // B OnStage kOne

  return 0;
}

或者,您可以使用標簽調度來調度適當的非模板成員 function,而不是使用StageTag的特化:

#include <iostream>
#include <vector>

enum class StageTag { kOne, kTwo, kThree };

class A {
private:
  template <StageTag> struct ATag {};

public:
  template <StageTag kTag> void OnStage() const { OnStage(ATag<kTag>{}); }

private:
  void OnStage(ATag<StageTag::kOne>) const { std::cout << "A OnStage kOne\n"; }

  void OnStage(ATag<StageTag::kTwo>) const { std::cout << "A OnStage kTwo\n"; }

  void OnStage(ATag<StageTag::kThree>) const {
    std::cout << "A OnStage kThree\n";
  }
};

class B {
private:
  template <StageTag> struct BTag {};

public:
  template <StageTag kTag> void OnStage() const { OnStage(BTag<kTag>{}); }

private:
  void OnStage(BTag<StageTag::kOne>) const { std::cout << "B OnStage kOne\n"; }

  void OnStage(BTag<StageTag::kTwo>) const { std::cout << "B OnStage kTwo\n"; }

  void OnStage(BTag<StageTag::kThree>) const {
    std::cout << "B OnStage kThree\n";
  }
};

template <StageTag kTag, typename T> void ForAll(const std::vector<T> &vector) {
  for (auto &item : vector) {
    item.template OnStage<kTag>();
  }
}

int main() {
  const std::vector<A> va(3);
  const std::vector<B> vb(2);

  ForAll<StageTag::kThree>(va);
  // A OnStage kThree
  // A OnStage kThree
  // A OnStage kThree

  ForAll<StageTag::kOne>(vb);
  // B OnStage kOne
  // B OnStage kOne

  return 0;
}

您可能還想查看 Curiously Recurring Template Pattern來強制執行您的 static 接口。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM