为什么 std::visit 不能消除模板重载的歧义

问题描述 投票:0回答:1

我正在尝试构建一个命令模式,其中每个命令都可以访问定义的接口。接收者实现一个或多个这些接口,然后可以通过应用于它们的 CommandList 来接收命令。我已包含下面的代码和编译器资源管理器链接。我希望两个模板都能应用,创建一个重载集,然后 std::visit (在 lambda 的帮助下)能够根据参数消除歧义,但这显然不是它的工作原理。有人可以解释为什么这不起作用,以及我必须解决哪些选项吗?谢谢!

<source>: In instantiation of 'void MultiReceiver<Interface, Interfaces>::applyCommandList(CommandList) [with Interface = ICatalogue; Interfaces = {IInventory}; CommandList = std::vector<std::variant<std::shared_ptr<Command<ICatalogue> >, std::shared_ptr<Command<IInventory> > >, std::allocator<std::variant<std::shared_ptr<Command<ICatalogue> >, std::shared_ptr<Command<IInventory> > > > >]':
<source>:110:26:   required from here
<source>:41:53: error: request for member 'applyCommand' is ambiguous
   41 |                 std::visit([=,this](auto& c){ this->applyCommand(*c); }, command);
      |                                               ~~~~~~^~~~~~~~~~~~
<source>:30:14: note: candidates are: 'void Receiver<Interface>::applyCommand(Command<Receiver<Interface> >) [with Interface = IInventory]'
   30 |         void applyCommand(Command<Receiver> command) {
      |              ^~~~~~~~~~~~
<source>:30:14: note:                 'void Receiver<Interface>::applyCommand(Command<Receiver<Interface> >) [with Interface = ICatalogue]'
Compiler returned: 1

天旋地转

#include <cinttypes>
#include <functional>
#include <memory>
#include <iostream>
#include <vector>
#include <variant>
#include <unordered_map>

template <class Receiver>
class Command {
    public:
        virtual void execute(Receiver& receiver) = 0;
};

class Product {
    public:
        Product(std::string name): _name(name) {};
        std::string getName() const { return _name; };
        bool operator==(const Product&) const = default;
    private:
        std::string _name;
};

template<class ...Receivers>
using CommandList = std::vector<std::variant<std::shared_ptr<Command<Receivers>>...>>;

template <class Interface>
class Receiver {
    public:
        void applyCommand(Command<Receiver> command) {
            command.execute(*this);
        }
};

template <class Interface, class ...Interfaces>
class MultiReceiver: public Receiver<Interface>, public Receiver<Interfaces>... {
    public:
        using CommandList = ::CommandList<Interface, Interfaces...>;
        void applyCommandList(CommandList commands) {
            for(const auto& command: commands ) {
                std::visit([=,this](auto& c){ this->applyCommand(*c); }, command);
            }
        };
};

class ICatalogue { 
    public:
        class AddProductCommand;
    private:
        virtual void addProduct(const Product& product) = 0;
};

class IInventory {
    public:
        class AddItemsCommand;

    private:
        virtual void addItems(Product product, uint32_t quantity) = 0;
};

class ICatalogue::AddProductCommand: public Command<ICatalogue> {
    public:
        AddProductCommand(Product product): _product { product } {};
        void execute(ICatalogue& catalogue) {
            catalogue.addProduct(_product);
        }
    private:
        Product _product;
};

class IInventory::AddItemsCommand: public Command<IInventory> {
    public:
        AddItemsCommand(Product product, uint32_t quantity): _product {product}, _quantity{quantity} {};
        void execute(IInventory& inventory) {
            inventory.addItems(_product, _quantity);
        }
    private:
        Product _product;
        uint32_t _quantity;
};

class Catalogue: public ICatalogue {
    private:
        void addProduct(const Product& product) override {
            _product.push_back(product);            
        }
        std::vector<Product> _product {};
};

auto productHash = [](const Product& p){ return std::hash<std::string>{}(p.getName()); };

class Inventory: public IInventory {
    private:
        void addItems(Product product, uint32_t quantity) override {
            _inventory[product] += quantity;
        };
        std::unordered_map<Product, uint32_t, decltype(productHash)> _inventory {10, productHash};
};

class Shop: public Catalogue, public Inventory, public MultiReceiver<ICatalogue, IInventory> {
};

int main() {
    Product banana { "Banana" };
    Shop::CommandList commandList {
        std::make_shared<ICatalogue::AddProductCommand>(banana),
        std::make_shared<IInventory::AddItemsCommand>(banana, 12)
    };
    Shop shop;
    shop.applyCommandList(commandList);

    return 0;
}
polymorphism c++20 visitor-pattern command-pattern
1个回答
0
投票

这与

std::visit
无关,也与重载解析无关。

this->applyCommand
本身的名称查找失败了。您的类有多个基类,这些基类具有不同的
applyCommand
成员函数,因此名称查找不明确。类成员的名称查找要求仅在基类之一中明确找到名称。

如果您的目的是使基类的每个成员都可用于派生类中的重载解析,那么您需要在派生类中使用

using
声明显式地使它们可用:

using Receiver<Interface>::applyCommand;
using Receiver<Interfaces>::applyCommand...;

但是,它会导致重载决策失败,因为没有一个重载是可行的。您尝试将

Command<SomeInterface>
传递给
applyCommand
,但其参数需要
Command<Receiver<SomeInterface>>

© www.soinside.com 2019 - 2024. All rights reserved.