提问人:Richard Vodden 提问时间:8/7/2023 最后编辑:Richard Vodden 更新时间:8/7/2023 访问量:64
为什么 std::visit 不能消除模板化重载的歧义
Why can't std::visit disambiguate templated overloads
问:
我正在尝试构建一个命令模式,其中每个命令都可以访问定义的接口。接收器实现一个或多个这些接口,然后可以通过应用 CommandLists 来获得命令。我包含了下面的代码和一个编译器资源管理器链接。我希望这两个模板都能应用,创建一个重载集,然后 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;
}
答:
4赞
user17732522
8/7/2023
#1
这与过载解决方案无关,也与过载解决方案无关。std::visit
失败的是名称查找本身。您的类具有多个具有不同成员函数的基类,因此名称查找是不明确的。类成员的名称查找要求仅在其中一个基类中明确找到该名称。this->applyCommand
applyCommand
如果打算使基类的这些成员中的每一个都可用于派生类中的重载解析,则需要通过派生类中的声明显式地使它们可用:using
using Receiver<Interface>::applyCommand;
using Receiver<Interfaces>::applyCommand...;
但是,它将无法解决重载问题,因为所有重载都不可行。您正在尝试传递给 ,但其参数需要 .Command<SomeInterface>
applyCommand
Command<Receiver<SomeInterface>>
评论
0赞
Richard Vodden
8/8/2023
那真是太完美了,谢谢@user17732522!为了让它工作,我必须确保接收器继承了它正在接收的接口(解决您的第二点),然后添加您描述的子句。[godbolt.org/z/j59rxjGoW](工作代码在这里)using
1赞
Ted Lyngmo
8/9/2023
@RichardVodden该链接已断开,则应为:godbolt.org/z/j59rxjGoW。简化可以是只有可变参数包,然后只需要一个等。如果有人想要没有示例,您将收到一条非常清晰的错误消息。MultiReceiver
using ...
static_assert
MultiReceiver
Interface
上一个:语言翻译注释处理
评论
virtual