Более элегантный способ заставить функцию-член C ++ изменять различные переменные-члены на основе параметра шаблона? - PullRequest
2 голосов
/ 02 июня 2010

Сегодня я написал некоторый код, который должен был добавлять элементы в различные переменные контейнера в зависимости от типа параметра шаблона. Я решил это, написав вспомогательный класс друга, специализирующийся на собственном параметре шаблона, который имел переменную-член исходного класса. Это спасло меня от нескольких сотен строк повторения, не добавляя много сложностей. Тем не менее, это казалось клуджем. Я хотел бы знать, есть ли лучший, более элегантный способ.

Приведенный ниже код является сильно упрощенным примером, иллюстрирующим проблему и мое решение. Компилируется в g ++.

#include <vector>
#include <algorithm>
#include <iostream>

namespace myNS{

  template<class Elt>
  struct Container{
    std::vector<Elt> contents;
    template<class Iter>
    void set(Iter begin, Iter end){
      contents.erase(contents.begin(), contents.end());
      std::copy(begin, end, back_inserter(contents));
    }
  };


  struct User;

  namespace WkNS{
    template<class Elt>
    struct Worker{
      User& u;

      Worker(User& u):u(u){}

      template<class Iter>
      void set(Iter begin, Iter end);
    };
  };

  struct F{ int x; explicit F(int x):x(x){} };
  struct G{ double x; explicit G(double x):x(x){} };

  struct User{
    Container<F> a;
    Container<G> b;

    template<class Elt>
    void doIt(Elt x, Elt y){
      std::vector<Elt> v; v.push_back(x); v.push_back(y);
      Worker<Elt>(*this).set(v.begin(), v.end());
    }

  };


  namespace WkNS{
    template<class Elt> template<class Iter>
    void Worker<Elt>::set(Iter begin, Iter end){
      std::cout << "Set a." << std::endl;
      u.a.set(begin, end);
    }

    template<> template<class Iter>
    void Worker<G>::set(Iter begin, Iter end){
      std::cout << "Set b." << std::endl;
      u.b.set(begin, end);
    }
  };

};

int main(){
  using myNS::F; using myNS::G;
  myNS::User u;
  u.doIt(F(1),F(2));
  u.doIt(G(3),G(4));
}

User - это класс, который я писал.

Worker мой класс помощника. У меня есть это в его собственном пространстве имен, потому что я не хочу, чтобы это вызывало проблемы вне myNS.

Container - это контейнерный класс, определение которого я не хочу изменять, но используется User в своих переменных экземпляра.

doIt<F> должен изменить a. doIt<G> должен изменить б.

F и G открыты для ограниченной модификации, если это приведет к более элегантному решению. (Как пример одной такой модификации, в реальном приложении конструктор F принимает фиктивный параметр, чтобы он выглядел как конструктор G и избавил меня от повторения.)

В реальном коде Worker является другом User, а переменные-члены являются закрытыми. Чтобы сделать пример проще для написания, я сделал все публичным. Однако решение, которое требует публичности, на самом деле не отвечает на мой вопрос.

Учитывая все эти предостережения, есть ли лучший способ написать User::doIt?

1 Ответ

1 голос
/ 02 июня 2010

Прочитав комментарий Эмиля Кормье, я подумал о том, как не повторяться, а также исключить класс Worker: создать две тривиальные не шаблонные функции doIt для F и G каждый вызов третьей шаблонной функции doIt с переменной для изменения, передаваемой в качестве параметра. Вот модифицированный код.

#include <vector>
#include <algorithm>
#include <iostream>

namespace myNS{

  template<class Elt>
  struct Container{
    std::vector<Elt> contents;
    template<class Iter>
    void set(Iter begin, Iter end){
      contents.erase(contents.begin(), contents.end());
      std::copy(begin, end, back_inserter(contents));
    }
  };

  struct F{ int x; explicit F(int x):x(x){} };
  struct G{ double x; explicit G(double x):x(x){} };

  struct User{
    Container<F> a;
    Container<G> b;

    template<class Elt>
    void doIt(Elt x, Elt y, Container<Elt>& cont, const char*name){
      std::vector<Elt> v; v.push_back(x); v.push_back(y);
      cont.set(v.begin(), v.end());
      std::cout << "Set " << name << std::endl;
    }

    void doIt(F x, F y){ doIt(x,y,a,"a"); }

    void doIt(G x, G y){ doIt(x,y,b,"b"); }

  };

}

int main(){
  using myNS::F; using myNS::G;
  myNS::User u;
  u.doIt(F(1),F(2));
  u.doIt(G(3),G(4));
}
...