4

我想编写一个模板函数,如果传递的类型是从另一个类的任何模板实例派生的,则该函数以一种方式运行,如果不是,则以另一种方式运行。

我认为下面的代码捕获了我想做的事情。double不幸的是,Caller 为和打印“通用” Derived

#include <iostream>

template <typename T>
struct Base
{
};

struct Derived
:
    public Base<int>
{
};

template <typename T>
void Foo(const T&)
{
    std::cout << "generic" << std::endl;
}

template <typename T>
void Foo(const Base<T>&)
{
    std::cout << "derives from Base<T>" << std::endl;
}

template <typename T>
void Caller(const T& t)
{
    Foo(t);
}

int main()
{
    double x;
    Caller(x);

    Derived d;
    Caller(d);

    return 0;
}

(请注意,调用者不知道它Base的参数可能来自哪个实例化。)

4

3 回答 3

2

It's calling the const T& overload because its a better match than const base<T>&. The reason is because calling the first requires no conversions and the second requires a derived-to-base conversion.

Here's a quick hack that shows you how it can be done (note the introduced base class):

#include <iostream>
#include <type_traits>

struct EvenMoreBase {};

template <typename T>
struct Base : EvenMoreBase
{
};

struct Derived
:
    public Base<int>
{
};

template <typename T>
typename std::enable_if<!std::is_base_of<EvenMoreBase, T>::value>::type
Foo(const T&)
{
    std::cout << "generic" << std::endl;
}

template <typename T>
void Foo(const Base<T>&)
{
    std::cout << "derives from Base<T>" << std::endl;
}

template <typename T>
void Caller(const T& t)
{
    Foo(t);
}

int main()
{
    double x;
    Caller(x);

    Derived d;
    Caller(d);

    return 0;
}
于 2013-09-26T07:37:11.367 回答
2

如果您能够使用 C++11(或<type_traits>一般情况下),以下也是一种可能的解决方案,不仅涵盖 types T : Base<T>,即 CRTP 的实例,而且还T : Base<U>没有其他基类,如您的示例中所要求的。

#include <iostream>
#include <type_traits>

template <typename T>
struct Base
{
  typedef T base_value_type;
};

struct Derived : public Base<int>
{
};

template <typename T, typename = T>
struct IsDerived
{
  static const bool value = false;
};

template <typename T>
struct IsDerived<T, typename std::enable_if<std::is_base_of<Base<typename T::base_value_type>, T>::value, T>::type>
{
  static const bool value = true;
};


template <typename T>
void Caller(const T&)
{
  std::cout << IsDerived<T>::value << std::endl;
}

int main()
{
  Caller(double());  // false
  Caller(Derived()); // true

  return 0;
}

请注意typedef T base_value_type- 可以随意调用。这个想法是每个T派生的类型Base<U>都可以利用基础模板参数的知识。有没有没关系T == U。尝试替换第二个参数将失败,只要您传入一个T没有的参数typedef T base_value_type,因此不会生成此特定参数的特化T

编辑:处理您的评论后,并受到我发布的线程的启发,我试图U在检查一些时间类型时以某种方式提取一些基本参数T : Base<U>。我认为这不能以您想要的方式完成,即您通过任何内容T然后提取U。但是,您可以做两件事。

简单的解决方案:如果您可以控制派生类的实现方式,而不是typedef在基类中添加 a,只需在派生类中添加相应的 typedef:

template <typename BaseParamType>
class Derived : public Base<BaseParamType>
{
public:
  typedef BaseParamType base_param_type;
}

或者,如果您不希望派生类也成为类模板,只需将类型硬编码到类型中(您已经知道基本参数的类型):

class Derived : public Base<int>
{
public:
  typedef int base_param_type;
}

更多涉及的解决方案:至少对于可能的预期子集U,您可以执行以下操作:

template <typename DerivedType,
          typename BaseParamType = DerivedType,
          bool   = std::is_base_of<Base<BaseParamType>, DerivedType>::value>
struct Extract
{
  typedef BaseParamType type;
};

template <typename T, typename U>
struct Extract<T, U, false>;

int main()
{
  Extract<DerivedCRTP>::type;     // CRTP - trivial
  Extract<Derived, int>::type;    // type == int, Derived is derived from Base<int>
  Extract<Derived, double>::type; // compile-time error, undefined template

  return 0;
}

这不像将某个类型的实例传递给推导模板函数并神奇地拥有它那样方便,但您至少可以测试某个类型是否T派生自Base<U>,如果不是,则获得编译时错误。

于 2013-09-26T09:43:01.163 回答
0

Since the base class has to be a concrete class (not a template), it is not possible to know whether it is a template or a non-template class.

In another words :

struct A1 : public B1
{};

struct A2 : public B2<int>
{};

in both of these cases both base classes are concrete types.

于 2013-09-26T07:37:57.750 回答