首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++ 11模板变量设计

C++ 11模板变量设计
EN

Stack Overflow用户
提问于 2014-05-15 20:45:43
回答 4查看 433关注 0票数 3

我有很多抽象类字母的子类,如A、B、C、D等。字母有一个整数id变量,每个字母子类都分配一个唯一的ID。

然后我又上了一堂课,叫它字母。字母表

代码语言:javascript
复制
list<shared_ptr<Letter>> 

成员。问题是..。我想优雅地添加字母的B和C或其他子类到特定的字母实例中。我认为最方便的方法是以某种方式使用子类的整数id。换句话说,我希望能够有类似于Alphabet.addLetter(int )的东西,所以如果我做了字母表1.add(14),它将以某种方式将H类的shared_ptr添加到列表中。

是否有一种优雅的方法来做到这一点,避免在每次添加或删除B、C、D、E等类时需要不断更新的大型if语句?我希望有某种模板解决方案,但我不太熟悉诸如工厂和模板这样的高级c++概念。我想要的是某种向量/地图,将我的I转换成类名,这样我就可以做一些类似的事情。

代码语言:javascript
复制
list.push_back(shared_ptr<classVector(i)>(new classVector(i))

或者诸如此类的东西,尽管我不知道这是否可能。

谢谢!

附注:我选择了Alphabet的例子,因为我不想给出不必要的细节。显然,我不是在用这样愚蠢的方式设计字母,lol。

编辑:我正在努力使这件事有意义。我的目标是能够非常迅速地创建新的信函子类,只需付出最少的努力。我想避免输入看起来像.

代码语言:javascript
复制
list.push_back(shared_ptr<X>(...));

每次我写一封新的信。这有意义吗?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2014-05-15 21:14:00

如果我正确地理解了您,使用所谓的工厂模式,这是相对容易的。

如果可以列出所有派生类型:

信件标题:

代码语言:javascript
复制
struct Letter {
    enum LetterEnum {LetterA, LetterB, LetterC, LetterCount};
    
    virtual ~Letter() {} //base types should always have virtual destructor
    virtual void foo() = 0;
  
    static std::unique_ptr<Letter> construct(LetterEnum c);
};

实现头:

代码语言:javascript
复制
struct A : Letter {
  void foo() override;
};

struct B : Letter {
  void foo() override;
};

struct C : Letter {
  void foo() override;
};

信件正文:

代码语言:javascript
复制
std::unique_ptr<Letter> Letter::construct(Letter::LetterEnum c) 
{
    switch(c) {
    case Letter::LetterA : return make_unique<A>();
    case Letter::LetterB : return make_unique<B>();
    case Letter::LetterC : return make_unique<C>();
    default: throw ...;
    }
}

用法:

代码语言:javascript
复制
int main() {
    char c;
    std::cin >> c;
    //get a letter of the derived type associated with the letter entered
    std::unique_ptr<Letter> ptr = Letter::construct(c);    
}

如果不能列出所有派生类型:

允许派生类型向Letter类注册,然后Letter可以使用它来创建每个派生类型。这样,添加和删除派生类型不涉及对任何其他文件的更改。简单!

代码语言:javascript
复制
struct Letter {
    virtual ~Letter() {} //destructor is always virtual when inheretence is involved
    ....
    //this is a "shared" function in the Letter class itself
    //it takes a letter, and returns a dynamically allocated instance
    //of the derived type corresponding with that letter
    static std::unique_ptr<Letter> construct(char c);
    //this typedef represents the actual function that returns 
    //each dynamically allocated derived type
    typedef std::function<std::unique_ptr<Letter>()> letter_ctor;
    //this is a "shared" function in the Letter class itself
    //it takes a letter, and a function that creates derived types, 
    //and saves them inside the container ctors
    static bool register(char c, letter_ctor func);
private:
    //this is a "shared" member in the Letter class.  
    //There is only one shared by all of the Letters.  Like a global.
    //When you give it a letter, it gives you a function.
    //and is VERY fast for large numbers of entries
    static std::unordered_set<char,letter_ctor> ctors;
};

在您的实现文件中:

代码语言:javascript
复制
//here's the function that derived types register themselves with
//pretty straightforward, just inserts the pair into the unordered_map
bool Letter::register(char c, Letter::letter_ctor func)
{return Letter::ctors.insert(std::make_pair(c,std::move(func))).second;}

//and here's the function that creates the derived types
//it checks if the letter is in the unordered_map
//if the letter isn't there, it throws an exception
//otherwise, it calls the function associated with that letter
//which creates the derived type on the heap, and returns a pointer to it
std::unique_ptr<Letter> Letter::construct(char c) 
{
     auto it = Letter::ctors.find(c);
     if (it == Letter::ctors.end())
         throw ...;
     return it->second(); //construct that letter
}

然后派生类型执行以下操作:

代码语言:javascript
复制
//you know this part
struct LetterA : public Letter 
{
   ....
};
//derived types have to register themselves:
//this is a global, so when the program loads, it automatically calls this
//even before main runs*
//it registers the letter 'A' and a function that creates a LetterA class on the heap
static bool registerA = Letter::register('A', [](){return make_unique<LetterA>();});

然后,您可以很容易地创建树派生类型!

代码语言:javascript
复制
int main() {
    char c;
    std::cin >> c;
    //get a letter of the derived type associated with the letter entered
    std::unique_ptr<Letter> ptr = Letter::construct(c);
}

*它并不总是在main之前被调用。如果有问题,将bool init_A();放在A头中,bool init_A(){return true;}放在A实现文件中,在主文件中放置static bool AInit=init_A();,这将强制执行它。然而,在实践中,这几乎是从来不需要的。

顺便提一句,这取决于有一个make_unique,它应该在C++11中,但是由于疏忽而被忽略了。它将在C++14中。同时,请使用以下命令:

代码语言:javascript
复制
template<class T, class...Us>
std::unique_ptr<T> make_unique(Us&&...us) 
{return std::unique_ptr<T>(new T(std::forward<Us>(us)...));}
票数 4
EN

Stack Overflow用户

发布于 2014-05-15 21:05:40

这一点很难理解,但我认为您想要的是以下内容:

代码语言:javascript
复制
// where make_unique<> is from C++14 in std:: or like:
template <typename T, typename ... TArgs>
std::unique_ptr<T> make_unique(TArgs &&... args) {
  return std::unique_ptr<T>(new T(std::forward<TArgs>(args)...));
}

struct Letter {
  virtual ~Letter() { }
  virtual void foo() = 0;
};
template <unsigned int N> struct LetterCode; // Note: no default implementation!

struct Alphabet {
  // Indexed access, if you'll have 1 of each type max:
  std::vector<std::unique_ptr<Letter>> v;

  // If you don't need parameters, as mentioned in comments below ...
  template <unsigned int N>
  void addLetterN() {
    if (N > v.size() + 1) { v.resize(N + 1); }
    v[N] = make_unique<LetterCode<N>::type>(); // see below ...
  }

  // If your coding is complete from 0...N, this does the whole shebang.
  template <unsigned int N>
  void addLettersN() {
    addLetters<N - 1>();
    addLetterN<N>();
  }
  template <>
  addLettersN<0>() {
    addLetterN<0>();
  }
};

如果您需要反序列化之类的数字代码,并且永远不需要构造函数参数,您可以使用一个类型特征模板,如下面所示,静态地“注册”这些类型:

代码语言:javascript
复制
struct B : Letter {
  B(int n, bool b, char const *name);
  void foo() override;
};
template <> struct LetterCode<2> { using type = B; };

struct C : Letter {
  C(double d);
  void foo() override;
};
template <> struct LetterCode<3> { using type = C; };

void bar() {
  Alphabet a;
  a.addLetterN<2>();
  a.addLetterN<3>();

  // --OR--
  a.addLettersN<3>(); // will do 0...3 in one fell swoop.

  for (auto &i : a.v) {
    if (!i) { continue; } // v is sparse, unlike l
    i->foo();
}

如果需要传递广义构造函数参数,则可以使用完美转发,这是为类似这种情况而设计的,并消除了旧类型工厂对枚举ID等的需求:

代码语言:javascript
复制
struct Alphabet {
  std::list<std::unique_ptr<Letter>> l;

  // variadic factory that chucks new (shared_ptr) objects in the list.
  template <typename T, typename ... TArgs>
  void addLetter(TArgs && ... args) {
    l.push_back(make_unique<T>(std::forward<TArgs>(args)...));
  }
};

void baz() {
  Alphabet a;
  a.addLetter<B>(1, false, "pony");
  a.addLetter<C>(2.718281828);

  for (auto &i : a.l) {
    i->foo(); // can call virtual funcs here all you want ...
  }
}
票数 8
EN

Stack Overflow用户

发布于 2014-05-15 21:00:20

我的理解是,您希望创建其中一个类的实例,依赖于与创建实例的类相关的id。

如果是的话,请看一看工厂的图案。有很多工厂实现,也是基于模板递归扩展的打字员。

伪码:

代码语言:javascript
复制
Factory<A,B,C,D> fac; // the list must be changed, if some more classes comes and goes
id_type id;

list<base> l;

l.push_back=fac.Create(id);

自己实现这样一个类也很简单。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23688289

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档