【问题标题】:C++ Adding overloaded methods for specific template specializationsC++ 为特定模板特化添加重载方法
【发布时间】:2014-05-01 17:24:45
【问题描述】:

我有一个非常有趣的问题:我有两个模板类。一个可以采用任何模板参数,另一个更专业(对于这个玩具问题,我们会说它必须采用浮点数)。

template< class T >
class CanBeAnything{ /*...*/ };

template< class T >
class MustBeFloat{ static_assert(is_floating_point<T>::value, ""); /*...*/ };

现在我有了另一个模板类 Foo。 Foo 对它的模板参数没有任何限制,函数 foo 采用相同类型的 CanBeAnythingMustBeFloat。我希望在这里使用显式模板实例化,所以我只希望在我的模板参数为浮点时存在MustBeFloat 重载。

最简单的解决方案似乎是专门化 Foo,但我不喜欢跨两个类复制接口的想法。我想出了一个几乎可以工作的 CRTP 解决方案,有一个问题我会在稍后提到

/* Traits object to get the value_type out of foo */
template<class FooType>
class FooTraits{};

/* Helper parent class with floating-point only methods */
template<class Derived, bool isFloatingPoint>
class FooSpecialization {}

template<class Derived>
class FooSpecialization<Derived, true>
{
   typedef typename FooTraits<Derived>::value_type value_type;
public:
   void foo( MustBeFloat<value_type> & x );
};

/* Front-end interface */
template<class T>
class Foo : public FooSpecialization< Foo<T>, is_floating_point<T>::value >
{
   typedef FooSpecialization< Foo<T>, is_floating_point<T>::value > Parent;
   typedef typename FooTraits< Foo<T> >::value_type value_type;
public:
   void foo( CanBeAnything<value_type> & x );
private:
   friend class Parent;
};

template<class T>
class FooTraits< Foo<T> >
   { public:  typedef T value_type; };

所以问题来了:按原样,对foo( MustBeFloat&lt;value_type&gt; &amp; ) 的调用通过名称隐藏隐藏在子类中,编译器给我“没有对方法 foo 的匹配调用”错误。如果我添加行using Parent::foo; 将其关闭,则在实例化非浮点Foo 时出现“父类中不存在foo”错误,因为该方法不存在那么远。

有什么想法吗?如果有更优雅/更有效的解决方案,我可以抓取整个解决方案。

编辑:澄清一下:我在这里进行显式实例化,这就是为什么我需要该方法仅在我有浮点模板参数时才存在。

template class Foo<int>;
template class Foo<float>;

这会实例化每个类成员,因此依赖于不实例化某些方法的方法是行不通的。

EDIT2:好的,所以我想太多了。这是我要使用的解决方案:

template<class T>
class Foo
{
public:
   template<class T2>
   void foo( MustBeFloat<T2> & x ){ static_assert( std::is_same<T,T2>::value, ""); /* ... */}
   void foo( CanBeAnything<T> & x ){ /* ... */ }
};

template class Foo<int>;
template class Foo<float>;
template void Foo::foo<float>(MustBeFloat<float> &);

这一切都有效。耶!感谢那些帮助我找到这个解决方案并提出其他更具创造性的解决方案的人。

【问题讨论】:

  • 澄清一下:当 value_type 是浮点数时,您希望 Foo 同时具有 MustBeFloat 和 CanBeAnything 版本的 foo() 吗?但是,如果 value_type 不是浮点数,那么它应该只有 CanBeAnything 版本的 foo() ?
  • @qeadz 这就是意图,是的。抱歉,如果从描述中不清楚。
  • 好的。使用“foo”之类的例子总是很愚蠢,所以这个建议可能行不通。如果您只需要在 value_type 是浮点数时出现 foo() 的专用版本,那么根本不从 FooSpecialization 派生如何。只需在 Foo 类中有多个版本的函数 foo()。使用 enable_if 仅根据 value_type 编译您想要的版本。如果更清楚的话,我可以用代码 sn-p 在答案中输入它。
  • ...老实说,这是我第一次听说使用“enable_if”。我做了一个模型,这正是我需要的。如果您想将其复制粘贴为实际答案,我会将其标记为正确。
  • 您的问题已解决,因此我现在不太愿意花时间处理这些问题。如果有人发布了更详细的答案,请务必将其标记为正确 - 高质量的回复需要时间,并且总体上对这个网站有好处(我只是懒得一直这样做)。

标签: c++ templates overloading template-specialization name-hiding


【解决方案1】:

好的。所以我还没有完全测试这个,但如果你有回复,那么我会评论或修改这个建议。但这里有一些示例代码,应该在简化的测试用例中编译成 foo() 版本,并使其特定于父类使用的类型:

template< typename T >
class TestClass
{
  typedef struct PlaceholderType {};

public:
  template< typename T2 >
  typename std::enable_if< !std::is_same<T2, PlaceholderType>::value && std::is_same<T, float>::value, void >::type MyFunc( T2 param ) { std::cout << "Float"; }

  template< typename T2 >
  typename std::enable_if< !std::is_same<T2, PlaceholderType>::value && !std::is_same<T, float>::value, void >::type MyFunc( T2 param ) { std::cout << "Non-float"; }
};


int main(int argc, char* argv[])
{
  TestClass<int> intClass; // should only have the MyFunc(int) version available
  TestClass<float> floatClass; // should only have the MyFunc(float) version available

  intClass.MyFunc(5); // should output "Non-float"
  intClass.MyFunc(5.0f); // should output "Non-float"
  floatClass.MyFunc(2.0f); // should output "Float"
  floatClass.MyFunc(2); // should output "Float"
}

【讨论】:

  • 你的魔法奏效了。由于您首先提出了建议,因此我将选择此作为正确答案,但是将来阅读此内容的任何人都应该查看 Oktalist 建议的其他解决方案,因为它们也可以解决问题。
  • 只需一秒钟。我已经改进了它(比萨饼仍然到达我们的午餐会议,所以我有时间)。我提到的这个版本在技术上仍然不正确。我现在用更新的版本替换答案。
  • 这解决了一个与我正在做的不同的问题......我根本不想要一个非浮动版本。但是你确实给了我我正在使用的解决方案的想法......只需模板化函数,这样它就不会被类的其余部分实例化。没有 enable_if 必要。
【解决方案2】:

这可能就足够了:

template <class T, bool isFloat, class Other>
struct FooTraits;
template <class T, class Other>
struct FooTraits<T, true, Other> { typedef MustBeFloat<T> MaybeFloat; };
template <class T, class Other>
struct FooTraits<T, false, Other> { typedef Other MaybeFloat; };

template <class T>
class Foo
{
    template <class U> friend class FooTraits<U>;
    class PrivateType {};
public:
    typedef typename FooTraits<T,
                               std::is_floating_point<T>::value,
                               PrivateType>::MaybeFloat MaybeFloat;
    void foo(CanBeAnything<T>&);
    void foo(MaybeFloat&);
};

如果T 是浮点数,那么MaybeFloat 将是MustBeFloat&lt;T&gt; 的类型定义。否则,它将是 Foo 的私有成员类,因此 foo() 的调用者不可能合成该类型的左值。

【讨论】:

  • 这真是太聪明了。我将研究 enable_if 解决方案,但这是一种避免问题的有趣方式。它有效,这也很有帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-09
  • 1970-01-01
  • 2022-07-05
  • 2020-12-24
相关资源
最近更新 更多