boost::function向量兼容任意参数及占位符问题求助
Hey there, let's break down what's causing your compile error and walk through two solid solutions to support arbitrary function parameters in your command vector.
The Root Cause of Your Compile Error
The key issue here is that each distinct function signature creates a unique boost::function type. For example, boost::function<void()> (your working no-arg case) and boost::function<void(int)> are completely separate C++ types. Since standard vectors only hold elements of a single uniform type, trying to mix these different boost::function instances directly in a vector will throw a compiler error—exactly what you're seeing.
Solution 1: Type Erasure with a Polymorphic Base Class
This approach wraps different boost::function types in a common polymorphic interface, letting us store them in a vector of smart pointers to the base class. It's flexible and scales well if you need to add more function signatures later.
Step 1: Define the Abstract Command Base
#include <boost/function.hpp> #include <memory> #include <vector> #include <stdexcept> #include <type_traits> // Abstract base class for all commands class Command { public: virtual ~Command() = default; // Generic invocation interface: use boost::any to pass arguments virtual void invoke(const boost::any& args) = 0; };
Step 2: Template Implementation for Specific Signatures
// Template subclass that adapts a specific boost::function signature template<typename FuncSig> class CommandImpl : public Command { private: boost::function<FuncSig> func_; public: explicit CommandImpl(boost::function<FuncSig> func) : func_(std::move(func)) {} void invoke(const boost::any& args) override { try { // Handle no-arg functions if constexpr (std::is_invocable_v<decltype(func_)>) { func_(); } // Handle single-arg functions (extend this for multi-arg with tuples if needed) else if constexpr (std::is_invocable_v<decltype(func_), typename std::function<FuncSig>::argument_type>) { auto arg = boost::any_cast<typename std::function<FuncSig>::argument_type>(args); func_(arg); } else { throw std::runtime_error("Unsupported function signature for command"); } } catch (const boost::bad_any_cast& e) { throw std::runtime_error("Argument type mismatch: " + std::string(e.what())); } } }; // Helper to simplify creating Command instances template<typename FuncSig> std::unique_ptr<Command> make_command(boost::function<FuncSig> func) { return std::make_unique<CommandImpl<FuncSig>>(std::move(func)); }
Step 3: Usage Example
// Your existing commands, plus new ones with arguments void command1() { // No-arg logic } void command2(int value) { // Int-arg logic } void command3(const std::string& msg) { // String-arg logic } int main() { std::vector<std::unique_ptr<Command>> commands; // Add commands of different signatures commands.push_back(make_command<void()>(&command1)); commands.push_back(make_command<void(int)>(&command2)); commands.push_back(make_command<void(const std::string&)>(&command3)); // Invoke each command with appropriate arguments commands[0]->invoke(boost::any{}); // No args commands[1]->invoke(42); // Int arg commands[2]->invoke("Hello!"); // String arg return 0; }
Solution 2: Boost.Variant for Type-Safe Union
If you have a fixed set of function signatures you need to support, boost::variant lets you store different boost::function types in a type-safe union. The compiler will enforce that you handle all possible types when invoking commands.
Implementation & Usage
#include <boost/function.hpp> #include <boost/variant.hpp> #include <vector> #include <string> #include <boost/bind.hpp> // Define a variant that covers all your supported function signatures using CommandVariant = boost::variant< boost::function<void()>, boost::function<void(int)>, boost::function<void(const std::string&)> >; // Visitor to invoke each command type class CommandInvoker : public boost::static_visitor<void> { public: // Handle no-arg functions void operator()(const boost::function<void()>& func) const { func(); } // Handle int-arg functions void operator()(const boost::function<void(int)>& func, int arg) const { func(arg); } // Handle string-arg functions void operator()(const boost::function<void(const std::string&)>& func, const std::string& arg) const { func(arg); } }; int main() { std::vector<CommandVariant> commands; // Populate the vector with commands commands.push_back(boost::function<void()>(&command1)); commands.push_back(boost::function<void(int)>(&command2)); commands.push_back(boost::function<void(const std::string&)>(&command3)); // Invoke commands using the visitor boost::apply_visitor(CommandInvoker(), commands[0]); boost::apply_visitor(boost::bind(CommandInvoker(), _1, 42), commands[1]); boost::apply_visitor(boost::bind(CommandInvoker(), _1, "Hello!"), commands[2]); return 0; }
Which Solution Should You Choose?
- Type Erasure: Go with this if you need flexibility to add new function signatures without modifying existing code. It's more open-ended but requires handling type safety at runtime.
- Boost.Variant: Pick this if you have a known, fixed set of function signatures. It provides compile-time type safety and avoids dynamic allocation overhead.
内容的提问来源于stack exchange,提问作者drtyhlpr

