我正在嘗試創建一個類(類似于std::tuple),它可以保存對不同型別物件的可變數量的參考,并且可以將對特定成員函式的呼叫轉發給它的所有組成部分。下面是一個例子:
// g Test7.C -std=c 17 -o Test7
#include <iostream>
struct P1 { void operator()(void) const { std::cout<<"P1 "; } };
struct P2 { void operator()(void) const { std::cout<<"P2 "; } };
struct P3 { void operator()(void) const { std::cout<<"P3 "; } };
template <class... EmitterList> struct Emitters
{
inline void operator()(void) const { std::cout<<std::endl; }
};
template <class Emitter, class... EmitterList>
class Emitters<Emitter, EmitterList...> : public Emitters<EmitterList...>
{
public:
using Base = Emitters<EmitterList...>;
Emitters(const Emitter & e, const EmitterList & ... eList)
: emitter(e), Base(eList...) {}
inline void operator()(void) const { emitter(); this->Base::operator()(); }
private:
const Emitter & emitter;
};
int main(void)
{
P1 p1;
P2 p2;
P3 p3;
Emitters e0; e0(); // works
//Emitters e1{p1}; e1(); // does not work
//Emitters e2{p1,p2}; e2(); // does not work
//Emitters e3{p1,p2,p3}; e3(); // does not work
return 0;
}
期望是e1()輸出“P1 \n”(呼叫p1()),e2()輸出“P1 P2 \n”(呼叫p1(); p2()),并e3()輸出“P1 P2 P3 \n”(呼叫p1(); p2(); p3();)。但是代碼中有些地方不對:它編譯e0但不適用于其他情況。你能幫我理解我在這里做的不對以及如何解決嗎?
非常感謝您的幫助!
uj5u.com熱心網友回復:
看起來您正在嘗試進行部分專業化,并且您已經接近了。而不是這個
template <class... EmitterList> struct Emitters
{
inline void operator()(void) const { std::cout<<std::endl; }
};
考慮
template <class... Ts>
struct Emitters;
template <>
struct Emitters<> {
void operator()(void) const { std::cout<<std::endl; }
};
template <class Emitter, class... EmitterList>
class Emitters<Emitter, EmitterList...> : public Emitters<EmitterList...>
{
// Unmodified from your example ...
}
這里的第一個宣告說“有一種東西叫做Emitters,但我還沒有告訴你它是什么”。這是一個不完整的型別定義。然后我們定義兩種情況:一種是Emitters<>(空的,基本情況),另一種(遞回情況)當我們至少有一個引數時。
這些類現在可以編譯,但是使用起來不方便。由于語言的復雜性,建構式呼叫不如常規函式推斷模板引數那么好。所以像這樣的一行
Emitters e1{p1};
不夠好。我們可以明確
Emitters<P1> e1{p1};
但這只是毫無意義的冗長。相反,我們可以查看std::tuple,它有同樣的問題。標準庫定義了一個函式std::make_tuple,它只呼叫元組建構式。但由于它是一個普通函式,我們得到了模板引數推導。
template <typename... Ts>
Emitters<Ts...> make_emitters(const Ts&... args) {
return Emitters<Ts...>(args...);
}
現在我們可以在不指定模板引數的情況下創建實體
Emitters e0 = make_emitters(); e0();
Emitters e1 = make_emitters(p1); e1();
Emitters e2 = make_emitters(p1,p2); e2();
Emitters e3 = make_emitters(p1,p2,p3); e3();
現在您的示例應該按預期作業。
關于建構式的最后一點說明。您應該始終首先列出基類建構式,然后再列出任何欄位。編譯-Wall將警告你這一點。編譯器正在內部為您重新排序它們,因此最佳做法是將它們按正確的順序開始。而不是
Emitters(const Emitter & e, const EmitterList & ... eList)
: emitter(e), Base(eList...) {}
考慮
Emitters(const Emitter & e, const EmitterList & ... eList)
: Base(eList...), emitter(e) {} // Note: Base and emitter are switched
完整示例:
#include <iostream>
struct P1 {
void operator()(void) const {
std::cout << "P1 ";
}
};
struct P2 {
void operator()(void) const {
std::cout << "P2 ";
}
};
struct P3 {
void operator()(void) const {
std::cout << "P3 ";
}
};
template <class... Ts>
struct Emitters;
template <>
struct Emitters<> {
void operator()(void) const {
std::cout << std::endl;
}
};
template <class T, class... Ts>
struct Emitters<T, Ts...> : public Emitters<Ts...> {
public:
using Base = Emitters<Ts...>;
Emitters(const T& e, const Ts&... eList)
: Base(eList...), emitter(e) {}
void operator()(void) const {
emitter();
this->Base::operator()();
}
private:
const T& emitter;
};
template <typename... Ts>
Emitters<Ts...> make_emitters(const Ts&... args) {
return Emitters<Ts...>(args...);
}
int main(void) {
P1 p1;
P2 p2;
P3 p3;
Emitters e0 = make_emitters(); e0();
Emitters e1 = make_emitters(p1); e1();
Emitters e2 = make_emitters(p1,p2); e2();
Emitters e3 = make_emitters(p1,p2,p3); e3();
return 0;
}
uj5u.com熱心網友回復:
另一個答案很好,但您實際上不需要任何這些更改。部分專業化清理很好而且很優雅,我建議使用它,但你的原始代碼也能作業。而且這些make_...函式很笨重,而且在現代 C 中幾乎不需要。您需要的是一個扣除指南:
template <typename ... P>
Emitters(const P& ... ) -> Emitters<P...>;
就是這樣。
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/371196.html
