Короче говоря, я пытался найти, как это сделать, но, кажется, что-то упустил. Одно ограничение моей проблемы: Human.h не может измениться. Мы должны действовать с тем, что нам дано. Мне также сказали создать массив указателей на члены, чтобы решить, какую функцию нужно вызвать.
Вот что у меня есть:
Человек.ч
class Human
{
private:
void meleeAttack(std::string const& target);
void rangedAttack(std::string const& target);
void intimidatingShout(std::string const& target);
public:
void action(std::string const& action_name, std::string const& target);
};
Человек.cpp
#include "Human.h"
typedef void (Human::* Human_mem_fnPtr)(std::string target);
void Human::meleeAttack(std::string const& target)
{
std::cout << "Melee Attack performed on " << target << "!\n";
}
void Human::rangedAttack(std::string const& target)
{
std::cout << "Ranged Attack performed on " << target << "!\n";
}
void Human::intimidatingShout(std::string const& target)
{
std::cout << "Shout performed on " << target << "!\n";
}
void Human::action(std::string const& action_name, std::string const& target)
{
//error on initialization--expression must be an lvalue or function designation--but they ARE func designations...
Human_mem_fnPtr fnPtr[] = {&Human::meleeAttack(target), &Human::rangedAttack(target), &Human::intimidatingShout(target)};
}
Из того, что я нашел в Интернете, я иду в правильном направлении здесь. Что мне не хватает?
Похоже, хорошее место для std::map или std::unordered_map из std::string
s в std::functions.
«&Human::meleeAttack
» — это указатель на функцию-член. Что конкретно в вашем учебнике C++ привело вас к мысли, что «&Human::meleeAttack(target)
» — это то, чем оно должно быть?
другое примечание: typedef void (Human::* Human_mem_fnPtr)(std::string target);
ест std::string
. void Human::meleeAttack(std::string const& target)
ест const
ссылку на std::string
. Они должны совпадать.
@SamVarshavchik, для функции требуется аргумент. Это было моим рассуждением. Но я вижу, где вы не будете использовать это, пока не вызовете индекс. В любом случае, если я уберу это, я получу сообщение об ошибке Human_mem_fnPtr fnPtr[] = {&Human::meleeAttack...
, говорящее о проблеме с инициализацией типа.
@agile_flow Тип требует аргумента, и указатель на функцию должен ссылаться на функцию, которая имеет правильную сигнатуру, как определено в вашем typedef.
«Рассуждение» имеет очень мало значения, когда речь идет о C++. Важно то, как на самом деле работает C++, и именно здесь на помощь приходит хороший учебник. Поисковая система не заменяет учебник по C++. Там полно всякой херни, которую поисковая система с радостью вернет вам, потому что куча хлама включает в себя правильные ключевые слова. Попытки выучить самый сложный и сложный из используемых сегодня языков программирования, читая результаты поисковых систем, всегда заканчиваются плачевно.
Я использую Stroustrups «Язык программирования C++», но мне нужен был пример, который было бы легче читать. Спасибо за предложение, но мне все еще нужна помощь здесь. Инициализация Human_mem_fnPtr fnPtr[]
не работает, даже когда я использую &Human::meleeAttack
Вы не видите разницы между фактическим параметром, определенным для каждой функции-члена, «std::string const&
», и вашим Human_mem_fnPtr
утверждением, что вместо этого должен быть std::string
? Все должно быть на 100% правильно, и совпадение в C++ «достаточно близко» никогда не бывает достаточным. Мне также любопытно узнать, какая глава в Stroustrups предлагает создание ролевой игры типа D&D в качестве практической задачи кодирования, как, кажется, предлагает показанный код.
@SamVarshavchik Я понял. У меня был правильный const, мне просто не хватало ссылочного символа. Чувак, ты должен просто дать ответ без снисходительного характера своего ответа. Это было бы легкой репутацией для вас, но вместо этого вы предпочли быть грубым.
Примечание: иногда вы увидите людей, использующих приведения типов, чтобы функция соответствовала указателю на функцию. Эти люди либо тратят время на актеров, либо стреляют себе в лицо. Когда Сэм говорит, что параметры должны точно совпадать, он не шутит. Людям, играющим с актерами, скорее всего, не повезло, и они не видят проблемы, потому что программа выглядит так, как будто она работает. И он будет продолжать работать до самого неподходящего момента, например, когда босс на выставке демонстрирует вашу работу.
Дополнительное примечание: Действительно хорошее чтение об уходе и подаче указателей функций.
Ну, я не особо отчаянно нуждаюсь в карме прямо сейчас, но что именно произвело на вас "снисходительное" впечатление, можно конкретнее? Я всегда рад получить конструктивную обратную связь. Снисходительно ли указывать, что C++ требует, чтобы все соответствовало, 100%, правильно? Нет, это абсолютно верно, и это не снисходительно заявить. И это правда, что вы каким-то образом упустили разницу между простым string
и многословным std::string const &
. Все ошибаются, это нормально, и не снисходительно указать на какие-то очевидные наблюдения.
@SamVarshavchik говорит мне, что «рассуждение» имеет очень мало значения - вам не нужно принижать меня здесь. У меня есть учебник, и хороший — вы решили, что я пытаюсь выучить язык по гуглю. И я никогда не говорил, что пример проблемы взят из его книги - вы просто предоставляете здесь самоуверенный наполнитель, который на самом деле не отвечает на вопрос. Да, вы добрались до этого, но с комментариями, которые заставляют людей не чувствовать себя здесь желанными. Вы могли бы просто опубликовать ответ с лайком: It looks like your typedef declaration and variable initialization don't match argument wise. Here's what it should be...
Это не имело целью принизить вас. Это просто для того, чтобы указать, что когда дело доходит до C++, собственного ума редко бывает достаточно, чтобы разобраться во всем. Даже я не пытаюсь понять, чего я еще не знаю: я ищу это. После первого крупного обновления C++ в 2011 году я получил необходимый справочный материал и прочитал его. Я не пытался ничего "аргументировать". А я ничего не предполагал, я исходил из того, что вы написали: "из того, что нашел в сети".
Несколько моментов:
std::function<>
.map
или unordered_map
были бы гораздо лучшим вариантом с таким определением:
using ActionMap = std::unordered_map<const std::string, std::function<void(const std::string&)>;
При добавлении ваших функций на эту карту вы должны использовать что-то вроде следующего:
mActionMap["rangedAttack"] = std::mem_fn(&Human::rangedAttack);
Это даст вам более чистый и простой в обслуживании вариант и должен компилироваться чисто.
Обратите внимание, что std::mem_fn
требуется для переноса функции-члена класса.
Обновлено: в соответствии с вашим комментарием ниже я по-прежнему предлагаю использовать как можно больше современных конструкций С++.
using ActionFunc = std::function<void(const std::string&)>;
А потом:
ActionFunc actions[] = { std::mem_fn(&Human::rangedAttack), ...}
или:
std::array<ActionFunc> actions = ...
Я ценю ответ здесь, но это не соответствует ограничениям проблемы: > Мне также сказали создать массив указателей на члены, чтобы решить, какую функцию нужно вызвать.
@agile_flow Хммм ... массив все еще может быть std::array
вместо голого вида. Сопоставление индекса массива со строкой по-прежнему потребует какой-то реляционной таблицы... и я бы по-прежнему рекомендовал использовать std::function
для создания сигнатуры вашей функции и создания массива из них (с using
вместо typedef
) и вставки с помощью `std ::mem_fn' вызов.
Пример выше: godbolt.org/z/eTc5neWM5 Даже если вы не можете использовать map
, остальное должно быть полезно. Без map
задание "Глупое". Вам понадобится if
или switch
, чтобы преобразовать action_name
в индексы массива, и в этот момент вы можете просто вызвать методы <ругательств удаленных> напрямую.
map
из string
для индекса массива и массива указателей на методы. Чем больше вещей вы должны изменить, тем выше вероятность того, что вы облажаетесь. скажем, вы добавляете действие «заклинание». Вы должны добавить элемент в массив и на карту, и карта, уверенная, что <ругательство удалено> лучше соответствует массиву. Заставляя студентов усердно работать над своими заданиями, учителя на самом деле часто делают хуже программистов. Лень — это добродетель.
Ха, да, это другой кикер (я не упоминал). Множественные операторы if/switch не допускаются. Я понимаю, что это задание не ведет меня к лучшим практикам на данный момент — оно скорее пытается научить определенным языковым аспектам и решить стоящую передо мной проблему с помощью набора инструментов.
Комбинация этого ответа + комментарии была полезна. Я ценю ваши отзывы о моем пути к совершенствованию. Спасибо!
Нет if
гетто предполагает, что, возможно, ручное сопоставление в порядке. Вместо Human_mem_fnPtr fnPtr[]
рассмотрите struct action_mapper {std::string name, Human_mem_fnPtr action; }; and
action_mapper action[] = { {{"melee",&Human::meleeAttack}, ... };` Повторяйте это с помощью цикла for
, пока не дойдете до конца или name
не совпадет с action_name
. Цель состоит в том, чтобы сохранить ключ и значение в одном пакете, чтобы было сложнее что-то перепутать.
В случае, когда список возможностей действительно короток, цикл O(n) for
настолько глуп и настолько прост, что он, вероятно, быстрее, чем карта с ее O(lon(n)) интеллектом и дополнительными накладными расходами, необходимыми для поддержки дополнительных интеллектов. . Компьютеры действительно хороши в том, чтобы быть глупыми очень быстро, поэтому часто требуется удивительно длинный список, чтобы превзойти инерцию глупости.
struct
: godbolt.org/z/vac87W3s9 Намного проще.
Это не фактические обозначения функций... вы бы хотели &Human::meleeAttack и т. д. Я полагаю.