我想在拥有派生类实例的对象中获取基类的模板化类型。下面的代码片段不起作用,因为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的东西)。谢谢!
答案 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;
};