在拥有Derived实例的类中获取模板化类型的Base

时间:2016-01-05 05:33:31

标签: templates c++11 typetraits decltype

我想在拥有派生类实例的对象中获取基类的模板化类型。下面的代码片段不起作用,因为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的附加模板arg,如下所示:

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是float或string。不是一个好的解决方案,因为ArbitraryType完全由OneOfTheDerivedTypes的选择指定。

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

1 个答案:

答案 0 :(得分:0)

也许我误解了你的问题,但你不能只为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模板,则还可以使用某些元编程来检测类型。首先,声明(但不要定义)可以从T中推断出类型Base<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<T>的指针(推导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;
};