简体   繁体   English

使用 C++ 中基本 class 模板的多个 inheritance 解决过载歧义

[英]Resolving overloading ambiguity with multiple inheritance of base class templates in C++

Let's say I'm trying to create a Combine class that will be derived from the given base classes.假设我正在尝试创建一个从给定基类派生的Combine class。

template<typename ...Bases>
class Combine : public Bases... {};

And this works fine.这很好用。 For example, if I have class Foo and class Bar then class Combine<Foo, Bar> will implement all the methods from Foo and Bar .例如,如果我有 class Foo和 class Bar那么 class Combine<Foo, Bar>将实现FooBar的所有方法。 At least I thought so until I tried this:至少我是这么认为的,直到我尝试了这个:

struct ContainerProvider {
    std::vector<int> container{1, 2, 3};
};

struct ConstGetter : public virtual ContainerProvider {
    [[nodiscard]] const int &get(int index) const {
        return container[index];
    }
};

struct MutableGetter : public virtual ContainerProvider {
    int &get(int index) {
        return container[index];
    }
};

template<typename ...Bases>
class Combine : public Bases... {};

int main() {
    Combine<ConstGetter, MutableGetter> container;
    container.get(1); // Member 'get' found in multiple base classes of different types
}

In normal situations, I would just use using Super::method;在正常情况下,我只会使用using Super::method; , but here I don't know the names of derived methods. ,但在这里我不知道派生方法的名称。 In a perfect world, I could use something like this:在一个完美的世界里,我可以使用这样的东西:

template<typename ...Bases>
class Combine : public Bases... {
    using Bases::* ...;
};

But C++ does not allow this.但是 C++ 不允许这样做。

Is it possible to implement my Combine class somehow?是否可以以某种方式实现我的Combine class ? I'm pretty sure the compiler can get all the information to resolve this edge case, but I have no idea how to provide it to make it work.我很确定编译器可以获得所有信息来解决这个边缘情况,但我不知道如何提供它来使其工作。

I imagine that renaming ConstGetter::get() to ConstGetter::const_get is not an option?我想将ConstGetter::get()重命名为ConstGetter::const_get不是一种选择? However, this compiles: container.ConstGetter::get(); container.MutableGetter::get();但是,这会编译: container.ConstGetter::get(); container.MutableGetter::get(); container.ConstGetter::get(); container.MutableGetter::get(); , check it here ,在这里检查


As of why overload resolution doesn't work across classes, you could check this old post and the answer of this question .至于为什么重载决议不能跨类工作,你可以查看这个旧帖子和这个问题答案

Curious problem since get() defined only in one class would work perfectly, but in two?奇怪的问题,因为 get() 仅在一个 class 中定义可以完美地工作,但在两个? Error?错误? I was curious if there might be a core language defect since this problem seems solvable by the compiler, but I didn't see any ( http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html ).我很好奇是否可能存在核心语言缺陷,因为编译器似乎可以解决这个问题,但我没有看到任何问题( http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects. html )。

The ideal solution would probably be for MutableGetter to inherit from ConstGetter since, logically, a getter that allows mutation should also allow grabbing non-mutable versions.理想的解决方案可能是MutableGetterConstGetter继承,因为从逻辑上讲,允许突变的 getter 也应该允许获取非可变版本。

struct MutableGetter : public ConstGetter {
  using ConstGetter::get;

  int &get(int index) {
    return container[index];
  }
};

Here's the closest I could get:这是我能得到的最接近的:

#include <iostream>
#include <vector>

struct ContainerProvider {
  std::vector<int> container{1, 2, 3};
};

struct ConstGetter : public virtual ContainerProvider {
  [[nodiscard]] const int &get(int index) const {
    std::cout << "CONST" << std::endl;
    return container[index];
  }
};

struct MutableGetter : public virtual ContainerProvider {
  int &get(int index) {
    std::cout << "NON-CONST" << std::endl;
    return container[index];
  }
};

template <typename... Bases>
class Combine;

template <typename B1, typename B2, typename... Bases>
struct Combine<B1, B2, Bases...> : public B1, public Combine<B2, Bases...> {
  using B1::get;
  using Combine<B2, Bases...>::get;
};

template<typename B>
struct Combine<B> : B {
  using B::get;
};

int main() {
  Combine<ConstGetter, MutableGetter> container;
  std::cout << container.get(0) << std::endl;  // non-const
  const auto &const_container = container;
  std::cout << const_container.get(0) << std::endl;  // const
}

It really sucks for the Combine to have to know what member functions its parents expose, though.不过, Combine必须知道它的父级公开了哪些成员函数,这真的很糟糕。 I found one partial solution if you're willing to drop the idea of member functions and occasionally do a cast...: Use friend functions如果您愿意放弃成员函数的想法并偶尔进行演员表,我找到了一个部分解决方案......:使用朋友函数

#include <iostream>
#include <vector>

struct ContainerProvider {
  std::vector<int> container{1, 2, 3};
};

class ConstGetter : public virtual ContainerProvider {
  [[nodiscard]] const int &get(int index) const {
    std::cout << "CONST" << std::endl;
    return container[index];
  }

  friend [[nodiscard]] const int& get(const ConstGetter &self, int i) { return self.get(i);  }
};

class MutableGetter : public virtual ContainerProvider {
  int &get(int index) {
    std::cout << "NON-CONST" << std::endl;
    return container[index];
  }

  friend [[nodiscard]] const int &get(MutableGetter &self, int i) {
    return self.get(i);
  }
};

template <typename... Bases>
class Combine : public Bases... {};

int main() {
  Combine<ConstGetter, MutableGetter> container;
  std::cout << get((MutableGetter&)container, 0) << std::endl;
  const auto &const_container = container;
  std::cout << get(const_container, 0) << std::endl;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM