我有以下问题:
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派生,并调用不同的代码。 出于性能原因,“检测”应该在编译时进行。
答案 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!