Как идиоматически вызовите функции C ++ на основе значения переменного?

StackOverflow https://stackoverflow.com/questions/3418700

  •  26-09-2019
  •  | 
  •  

Вопрос

Предположим, у меня есть тип данных enum TreeTypes { TallTree, ShortTree, MediumTree }.

И я должен инициализировать некоторые данные, основанные на одном конкретном типе дерева.

В настоящее время я написал этот код:

int initialize(enum TreeTypes tree_type) {
    if (tree_type == TallTree) {
        init_tall_tree();
    }
    else if (tree_type == ShortTree) {
        init_short_tree();
    }
    else if (tree_type == MediumTree) {
        init_medium_tree();
    }
    return OK;
}

Но это какой-то тупые повторение кода. Я не использую никаких мощных возможностей C ++, таких как шаблоны.

Как я могу написать этот код лучше?

Спасибо, Бода Cydo.

Это было полезно?

Решение

Ваш код в порядке на два или три значения, но вы правы, вам нужна что-то более промышленная сила, когда у вас есть сотни из них. Два возможных решения:

  • Используйте классовую иерархию, а не Enums - затем вы можете использовать виртуальные функции и иметь работу компилятора, какую фактическую функцию звонить

  • Создать карту Enum -> Функция, которую вы инициализируете при запуске - вызовы функций, затем стали чем-то вроде map[enum]->func()

Шаблоны здесь не работают так хорошо, потому что вы пытаетесь принять решение во время выполнения, тогда как шаблоны делают свои вещи при компиляции времени.

Другие советы

Одним словом: наследство

class Tree { public: virtual void initialize() = 0; }

class ShortTree : public Tree {
public:
    virtual void initialize(){
        /* Short Tree specific code here */
    }
}

class MediumTree : public Tree {
public:
    virtual void initialize(){
        /* Medium Tree specific code here */
    }
}

class TallTree : public Tree {
public:
    virtual void initialize(){
        /* Tall Tree specific code here */
    }
}

Тогда везде, где вы хотите позвонить инициализировать, просто убедитесь, что указка или ссылка на полиморфизм, чтобы правильно работать:

Vector<Tree*> trees;
trees.push_back(new SmallTree());
trees.push_back(new MediumTree();
trees.push_back(new TallTree();

// This will call the tree specific code for each tree in the vector
for(vector<Tree*>::iterator tree = trees.begin(); tree!=trees.end(); ++tree)
    tree->initialize();

Используйте таблицу поиска, которая индексируется значениями Enum (при условии, что все функции имеют одинаковую подпись), т. Е.

enum TreeTypes { TallTree, ShortTree, MediumTree, MaxTreeTypes }

typedef void (*p_init_func)(void); 

p_init_func initialize_funcs[MaxTreeTypes] =
{
    &init_tall_tree, 
    &init_short_tree,
    &init_medium_tree
};

int initialize(enum TreeTypes tree_type)
{ 
    initialize_funcs[tree_type]();
    return OK; 
} 

Попробуйте оператор коммутатора:

int initialize(enum TreeTypes tree_type) {
    switch (tree_type) {
        case TallTree: 
            init_tall_tree();
            break;
        case ShortTree:
            init_short_tree();
            break;
        case MediumTree:
            init_medium_tree();
            break;
    }
    return OK;
}

Если эта инициализация действительно является единственным различием, то я не уверен, что какая-либо другая идиома улучшит ситуацию.

Вы можете подкласс из дерева и создать правильный вид предмета дерева ... но вам все равно нужно дифференцировать, какой из них можно создать, поэтому вы все равно должны быть подобными, если бы / остальные блокировки, где-то.

Тем не менее, если есть больше, чем просто инициализация, которая отличается, вам следует подкласс и использовать виртуальные функции для принятия различий между ними.

И шаблон, так как вы указали это в свои теги:

enum TreeTypes { Tall, Short, Medium };

struct TreeBase {
    // (...)
};

struct TallTree : public TreeBase {
    // (...)
};

struct ShortTree : public TreeBase {
    // (...)
};

struct MediumTree : public TreeBase {
    // (...)
};

template<TreeTypes N_type = Tall>
struct Tree : public TallTree {
    // (...)
};

template<>
struct Tree<Short> : public ShortTree {
    // (...)
};

template<>
struct Tree<Medium> : public MediumTree {
    // (...)
};

Таким образом, у вас есть отдельные классы для каждого типа дерева, который можно добраться до базового указателя. Удавая их в класс деревьев, позвольте вам сделать это:

Tree<Tall> tall_tree;
Tree<Short> short_tree;
Tree<Medium> medium_tree;
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top