繁体   English   中英

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

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

假设我正在尝试创建一个从给定基类派生的Combine class。

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

这很好用。 例如,如果我有 class Foo和 class Bar那么 class Combine<Foo, Bar>将实现FooBar的所有方法。 至少我是这么认为的,直到我尝试了这个:

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
}

在正常情况下,我只会使用using Super::method; ,但在这里我不知道派生方法的名称。 在一个完美的世界里,我可以使用这样的东西:

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

但是 C++ 不允许这样做。

是否可以以某种方式实现我的Combine class ? 我很确定编译器可以获得所有信息来解决这个边缘情况,但我不知道如何提供它来使其工作。

我想将ConstGetter::get()重命名为ConstGetter::const_get不是一种选择? 但是,这会编译: container.ConstGetter::get(); container.MutableGetter::get(); container.ConstGetter::get(); container.MutableGetter::get(); ,在这里检查


至于为什么重载决议不能跨类工作,你可以查看这个旧帖子和这个问题答案

奇怪的问题,因为 get() 仅在一个 class 中定义可以完美地工作,但在两个? 错误? 我很好奇是否可能存在核心语言缺陷,因为编译器似乎可以解决这个问题,但我没有看到任何问题( http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects. html )。

理想的解决方案可能是MutableGetterConstGetter继承,因为从逻辑上讲,允许突变的 getter 也应该允许获取非可变版本。

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

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

这是我能得到的最接近的:

#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
}

不过, Combine必须知道它的父级公开了哪些成员函数,这真的很糟糕。 如果您愿意放弃成员函数的想法并偶尔进行演员表,我找到了一个部分解决方案......:使用朋友函数

#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