【问题标题】:Get templated type of Base inside of class that owns instance of Derived在拥有 Derived 实例的类中获取 Base 的模板类型
【发布时间】:2016-01-05 05:33:30
【问题描述】:

我想在拥有派生类实例的对象中获取基类的模板化类型。下面的代码 sn-p 不起作用,因为 Base 及其 ArbitraryType 无法通过 DerivedString 引用。 (标有感叹号的线)。但是,它绝对可以从它自己的模板类型 (OneOfTheDerivedTypes) 中推断出来。在我的情况下,我打算继承具有已定义模板的 AnotherObject,因此我不想将返回类型硬编码为 GetSomethingFromThingy()。

// -------------------
// Provided by my API:
// -------------------
template <typename ArbitraryType>
class Base {
  virtual void DoSomething(ArbitraryType);
};

template <typename OneOfTheDerivedTypes>
class AnotherObject<OneOfTheDerivedTypes> {
  // Other functions that perform useful tasks are here so that
  // classes that inherit from AnotherObject need not implement them.
  void SomethingMagical();

  // A function with a specific return type.
  virtual DerivedString::Base::ArbitraryType GetSomethingFromThingy() = 0; /* ! */
 protected:
  OneOfTheDerivedTypes thingy;
};

// --------------------------------------
// Someone using my API would make these:
// --------------------------------------
class DerivedFloat : public Base<float> {
  void DoSomething(float) override;
};

class DerivedString : public Base<string> {
  void DoSomething(string) override;
};

class UsefulObject : public AnotherObject<DerivedString> {
  // Knows the required return type of GetSomethingFromThingy() without
  // needing to specify it as a template. Should throw compile-time error 
  // if you tried to override with a different return type. In other words,
  // forces return type to be string because of use of DerivedString.
  string GetSomethingFromThingy() override;
};

对此的一种解决方案是指定一个名为 ArbitraryType 的附加模板参数,如下所示:

template <typename OneOfTheDerivedTypes, typename ArbitraryType>
class AnotherObject<OneOfTheDerivedTypes> {
  virtual ArbitraryType GetSomethingFromThingy() = 0;
 protected:
  OneOfTheDerivedTypes thingy;
};

class UsefulObject<DerivedString, string> : public AnotherObject<DerivedString, string> {
  string GetSomethingFromThingy() override;
};

然后程序员必须指定这两个参数,其中 OneOfTheDerivedTypes 是 DerivedFloat 或 DerivedString,而 ArbitraryType 分别是浮点数或字符串。这不是一个好的解决方案,因为 ArbitraryType 完全由 OneOfTheDerivedTypes 的选择指定。

我认为可以通过让 Base 在公共函数中返回 ArbitraryType 的实例(称为 ReturnInstanceOfArbitraryType())并在 AnotherObject 中使用 decltype(OneOfTheDerivedTypes::ReturnInstanceOfArbitraryType()) 来避免额外的模板(AnotherObject 中的 ArbitraryType)。这似乎不优雅,因为 ReturnInstanceOfArbitraryType() 在其他情况下没有用(并且必须是公共的)。在这种情况下,正确的做法是使用特征类吗?有更好的解决方案吗? (仍然掌握了一些新的 C++11 东西)。谢谢!

【问题讨论】:

  • Base 内部,您可以稍后在派生类中使用typedef ArbitratyType TypeOfBase,您可以在第一个示例中使用TypeOfBase 而不是DerivedString::Base::ArbitraryType
  • 啊,是的。效果很好,请随时在下面回答。它坚持我使用'typename DerivedType::TypeOfBase',这有点不幸。似乎它应该能够从它的使用方式和它是一个 typedef 的事实来判断它是一个类型。我在这里阅读更多关于它的信息:stackoverflow.com/questions/7923369/…
  • 一个 using 声明可以帮助清理 typename 关键字: using TypeOfBase = typename DerivedType::TypeOfBase

标签: templates c++11 typetraits decltype


【解决方案1】:

也许我误解了你的问题,但你不能在Base 中添加一个typedef 吗?

template <typename ArbitraryType>
class Base {
  virtual void DoSomething(ArbitraryType);

  using parameter_type = ArbitraryType;
};

然后你可以参考一下:

template <typename OneOfTheDerivedTypes>
class AnotherObject {
  // A function with a specific return type.
  virtual typename OneOfTheDerivedTypes::parameter_type GetSomethingFromThingy() = 0;
};

然后派生类型中的override 将强制返回类型相同(或协变):

class UsefulObject : public AnotherObject<DerivedString> {
  string GetSomethingFromThingy() override;
};

如果您想要更用户友好的错误消息,也可以添加static_assert

class UsefulObject : public AnotherObject<DerivedString> {
  using base_parameter_type = typename AnotherObject<DerivedString>::parameter_type;
  static_assert(std::is_same<string, base_parameter_type>::value,
                "mismatched parameter types");

  string GetSomethingFromThingy() override;
};

如果您无法修改Base 模板,也可以使用一些元编程来检测类型。首先,声明(但不定义)一个可以从Base&lt;T&gt; 推导出类型T 的函数:

template<typename T> T* detect_base_parameter_type(Base<T>*);  // undefined

现在定义一个别名模板,它将派生类型之一作为其模板参数,并使用上面的函数查找其基类的模板参数:

template<typename DerivedT>
  using base_parameter_t = typename std::remove_pointer<
    decltype( detect_base_parameter_type(std::declval<DerivedT*>()) )
  >::type;

这使用 decltype 来检测调用 detect_base_parameter_type 的返回类型,并带有指向派生类型的指针。该指针将转换为指向Base&lt;T&gt; 的指针(推断T 用于DerivedT 的任何类型)并且函数的返回类型将为T*。然后我们用remove_pointer把它变成T

现在您可以在其他类中使用该别名模板:

template <typename OneOfTheDerivedTypes>
class AnotherObject {
  // A function with a specific return type.
  virtual base_parameter_t<OneOfTheDerivedTypes> GetSomethingFromThingy() = 0;
};

class UsefulObject : public AnotherObject<DerivedString> {
  using base_parameter_type = base_parameter_t<DerivedString>;
  static_assert(std::is_same<string, base_parameter_type>::value,
                "mismatched parameter types");

  string GetSomethingFromThingy() override;
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-07-09
    • 2011-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多