在C ++中选择重载模板函数时的优先级

时间:2009-08-26 06:23:18

标签: c++ templates methods overloading

我有以下问题:

class Base
{
};

class Derived : public Base
{
};

class Different
{
};

class X
{
public:
  template <typename T>
  static const char *func(T *data)
  {
    // Do something generic...
    return "Generic";
  }

  static const char *func(Base *data)
  {
    // Do something specific...
    return "Specific";
  }
};

如果我现在

Derived derived;
Different different;
std::cout << "Derived: " << X::func(&derived) << std::endl;
std::cout << "Different: " << X::func(&different) << std::endl;

我得到了

Derived: Generic
Different: Generic

但我想要的是,对于从Base派生的所有类,调用特定方法。 所以结果应该是:

Derived: Specific
Different: Generic

有什么方法可以重新设计X:func(...)来达到这个目标吗?

修改

假设作为参数提交的类是否是从Base派生的,X :: func(...)的调用者不知道它。所以Casting to Base不是一个选择。 事实上,整个事情背后的想法是X :: func(...)应该'检测'参数是否从Base派生,并调用不同的代码。 出于性能原因,“检测”应该在编译时进行。

6 个答案:

答案 0 :(得分:16)

我找到了一个非常简单的解决方案!

class Base
{
};

class Derived : public Base
{
};

class Different
{
};

class X
{
private:
  template <typename T>
  static const char *intFunc(const void *, T *data)
  {
    // Do something generic...
    return "Generic";
  }

  template <typename T>
  static const char *intFunc(const Base *, T *data)
  {
    // Do something specific...
    return "Specific";
  }

public:
  template <typename T>
  static const char *func(T *data)
  {
    return intFunc(data, data);
  }
};

效果很好,非常苗条! 诀窍是让编译器通过(否则无用的)第一个参数选择正确的方法。

答案 1 :(得分:7)

您必须使用SFINAE。在以下代码中,当且仅当您传递无法(隐式)转换为Base *的内容时,才能实例化第一个函数。第二个功能就是这样。

您可能希望阅读enable_if

#include <iostream>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits.hpp>

class Base {};
class Derived : public Base {};
class Different {};

struct X
{
    template <typename T>
    static typename boost::disable_if<boost::is_convertible<T *, Base *>,
        const char *>::type func(T *data)
    {
        return "Generic";
    }

    template <typename T>
    static typename boost::enable_if<boost::is_convertible<T *, Base *>,
        const char *>::type func(T *data)
    {
        return "Specific";
    }
};

int main()
{
    Derived derived;
    Different different;
    std::cout << "Derived: " << X::func(&derived) << std::endl;
    std::cout << "Different: " << X::func(&different) << std::endl;
}

答案 2 :(得分:1)

表达式:

X::func(derived)

意味着编译器将生成一个有效具有此签名的声明和代码:

static const char *func(Derived *data);

这比你的匹配更好:

static const char *func(Base *data);

模板函数将用于任何对typename合法的内容,例如您用作T的任何类,由于编译时策略,它将有效地排除函数的Base版本。

我的建议是在X中使用specialization作为您的特定类型,即:

template <typename T>
  static const char *func(T *data)
  {
    // Do something generic...
    return "Generic";
  }

template <>
  static const char *func(Derived *data) // 'Derived' is the specific type
  {
    // Do something specific...
    return "Specific";
  }

希望有效!

答案 3 :(得分:1)

如果你正在使用boost,你可以使用一些模板元编程来实现:

#include <boost/type_traits/is_base_of.hpp>

class X
{
private:
    template <typename T>
    static const char *generic_func(T *data)
    {
        // Do something generic...
        return "Generic";
    }

    template <typename T>
    static const char *base_func(T *data)
    {
        // Do something specific...
        return "Specific";
    }

public:
    template <typename T>
    static const char* func(T* data)
    {
        if (boost::is_base_of<Base, T>::value)
            return base_func(data);

        return generic_func(data);
    }
};

在编译时评估is_base_of元函数,优化器很可能会删除if函数中func的死分支。这种方法允许您拥有多个特定案例。

答案 4 :(得分:0)

只是强制转换为基础

X :: FUNC((基本*)及衍生)

它有效......

答案 5 :(得分:0)

我正在设置重叠enable_if的优先级,特别是为了回退调用STL容器方法,其中我的特征是诸如is_assignable is_insterable等等。在多个容器上有重叠的东西。

我想优先分配,如果它存在,否则使用插入迭代器。这是我提出的一个通用示例(由#boost irc频道中的一些方便的人改变了无限级别的优先级)。它的作用是优先级的隐式转换将重载排在另一个之下,否则这是一个同样有效的选项 - 消除歧义。

#include <iostream>
#include <string>

template <std::size_t N>
struct priority : priority<N - 1> {};

template <>
struct priority<0> {};

using priority_tag = priority<2>;

template <typename T> 
void somefunc(T x, priority<0>)
{
    std::cout << "Any" << std::endl;
}

template <typename T> 
std::enable_if_t<std::is_pod<T>::value >
somefunc(T x, priority<2>)
{
    std::cout << "is_pod" << std::endl;
}

template <typename T>
std::enable_if_t<std::is_floating_point<T>::value >
somefunc(T x, priority<1>)
{
    std::cout << "is_float" << std::endl;
}

int main()
{
    float x = 1;
    somefunc(x, priority_tag{});
    int y = 1;
    somefunc(y, priority_tag{}); 
    std::string z;
    somefunc(z, priority_tag{});
    return 0;
}

还有人建议在C ++ 14中我可以使用constexpr if语句来实现相同的功能,如果Visual Studio 2015支持它们则更加清晰。希望这会帮助其他人。

#include <iostream>
#include <string>

template <typename T>
void somefunc(T x)
{
    if constexpr(std::is_floating_point<T>::value) {
      static_assert(std::is_floating_point<T>::value);
      std::cout << "is_float" << std::endl;
    } else if constexpr(std::is_pod<T>::value) {
      static_assert(std::is_pod<T>::value);
      std::cout << "is_pod" << std::endl;
    } else {
      static_assert(!std::is_floating_point<T>::value);
      static_assert(!std::is_pod<T>::value);
      std::cout << "Any" << std::endl;
    }
}

int main()
{
    float x = 1;
    somefunc(x);
    int y = 1;
    somefunc(y); 
    std::string z;
    somefunc(z);
    return 0;
}

//感谢k-ballo @ #boost!