我正在研究以下想法:
存在一个通常具有多个模板参数的抽象模板化基类。该类定义了一个保证某些方法存在的合同,即方法getMax()。这种方法通常纯粹是虚拟的。除非存在特殊情况,其中可以给出合理的实现,而无需每次在派生类中手动实现它。所以基本上我想要实现的是(如果模板参数允许的话)(部分)实现抽象基类中已经存在的缩小方法。
我举了一个小例子来说明这个想法。 (注意:示例并不完美,例如std :: string的实现非常特殊,并且已经隐式强制TSize为std :: size_t)
#ifndef ABSTRACTBASE_H
#define ABSTRACTBASE_H
#include <type_traits>
#include <string>
#include <limits>
template <typename TSize, typename TVal1, typename TVal2>
class AbstractBase
{
public:
AbstractBase(){};
virtual ~AbstractBase() {};
virtual TSize getMax() const = 0; // <-- getMax should be generally
// purely virtual.
private:
TVal1 value1;
TVal2 value2;
};
//except when TVal1 is an arithmetic type in that case the following definition
//shall become active.
template <typename TSize,
typename TVal1,
typename TVal2,
typename = typename std::enable_if<std::is_arithmetic<TVal1>::value, TSize>::type>
TSize AbstractBase<TSize, TVal1, TVal2>::getMax() const
{
return std::numeric_limits<TVal1>::max();
}
//... or when TVal1 is a string where this defintion makes sense
template <typename TSize,
typename TVal1,
typename TVal2,
typename = typename std::enable_if<std::is_same<TVal1, std::string>::value, TSize>::type>
TSize AbstractBase<TSize, TVal1, TVal2>::getMax() const
{
return value1.max_size();
}
//... in all other cases the getMax() method shall stay purely virtual and an
//appropriate definition must be implemented inside a derived class
#endif //ABSTRACTBASE_H
#include "AbstractBase.h"
#include <string>
#include <iostream>
int main()
{
AbstractBase<int, int, int> arthBase();
AbstractBase<std::size_t, std::string, long> alphaBase();
std::cout << arthBase.getMax() << std::endl;
std::cout << alphaBase.getMax() << std::endl;
}
所以我想这里缺少的是一种实际上也将声明ov getMax()更改为虚拟的方法,尽管我不确定是否/如何使用type_traits这样做。
旁注:我还没有很好地处理类型特征。我知道它背后的SFINAE原理,它基本上表明如果替换模板参数失败,下面的代码将被排除在编译之外,而不是导致错误。如果负责启用/禁用方法的type_trait参数必须像我上面那样合并到类模板参数列表中,或者在单独的模板中提供类型特征参数是合法/可能的,我还没有发现参数列表。在这种情况下,我 guess 这是不可能的,因为enable_if语句测试必须在此上下文中声明/有效的类模板参数。
如果你使用真正复杂的类型特质魔法,我非常感谢你对该部分做出更精细的评论。
答案 0 :(得分:1)
这里讨厌的部分是字符串版本需要访问成员变量。因此,解决这个问题的方法是将成员放在最基类中:
template <typename TVal1, typename TVal2>
class Members {
protected:
TVal1 value1;
TVal2 value2;
};
将getMax()
外包给其他类型:
// pure case
template <typename TSize, typename TVal1, typename TVal2>
struct VirtualMax : Members<TVal1, TVal2> {
virtual TSize getMax() const = 0;
};
// integer case
template <typename TSize, typename TVal1, typename TVal2>
struct IntMax : Members<TVal1, TVal2> {
TVal1 getMax() const { return std::numeric_limits<TVal1>::max(); }
};
// string case
template <typename TSize, typename TVal1, typename TVal2>
struct StringMax : Members<TVal1, TVal2> {
size_t getMax() const {
return this->value1.max_size();
}
};
那么我们写一个类型特征:
template <typename TSize, typename TVal1, typename TVal2>
using Base_t = std::conditional_t<
std::is_same<TVal1, std::string>::value,
StringMax<TSize, TVal1, TVal2>,
std::conditional_t<
std::is_arithmetic<TVal1>::value,
IntMax<TSize, TVal1, TVal2>,
VirtualMax<TSize, TVal1, TVal2>
>
>;
然后使用该别名:
template <typename TSize, typename TVal1, typename TVal2>
class AbstractBase
: Base_t<TSize, TVal1, TVal2>
{ };
答案 1 :(得分:0)
After some tinkering and thanks to some ideas Barry gave me with his post above I took another shot at the problem myself. While Barrys answer provides a solution to the actual question I'd go and reply to myself to the question with "You don't.". The why Barry and me discussed in the comments:
So the much cleaner shot at this problem would be to keep the "AbstractBase" class purely abstract and combine type traits with inheritance deriving an ArithmeticBase class and a StringBase class using type traits to validate they only take valid template arguments. A solution could look like this:
#include <limits>
#include <string>
#include <type_traits>
#ifndef ABSTRACTBASE_H
#define ABSTRACTBASE_H
template <typename TSize,
typename TVal1,
typename TVal2>
class AbstractBase {
public:
virtual ~AbstractBase() {};
virtual TSize getMax() const = 0;
protected:
TVal1 value1;
TVal2 value2;
};
#endif //ABSTRACTBASE_H
#ifndef ARITHMETICBASE_H
#define ARITHMETICBASE_H
#include <limits>
#include <type_traits>
#include "AbstractBase.h"
template <typename TSize,
typename TVal1,
typename TVal2 = typename std::enable_if<std::is_arithmetic<TVal1>::value>::type>
class ArithmeticBase : public AbstractBase<TSize, TVal1, TVal2>
{
public:
virtual ~ArithmeticBase() {};
virtual TSize getMax() const { return std::numeric_limits<TVal1>::max(); };
};
#endif //ARITHMETICBASE_H
#ifndef STRINGBASE_H
#define STRINGBASE_H
#include <limits>
#include <type_traits>
#include <string>
#include "AbstractBase.h"
template <typename TSize,
typename TVal1,
typename TVal2 = typename std::enable_if<std::is_same<TVal1, std::string>::value>::type>
class StringBase : public AbstractBase<TSize, TVal1, TVal2>
{
public:
virtual ~StringBase() {};
virtual TSize getMax() const { return this->value1.max_size(); };
};
#endif //STRINGBASE_H
#include <string>
#include <iostream>
#include "ArithmeticBase.h"
#include "StringBase.h"
int main()
{
ArithmeticBase<int, int, int> arthBase;
StringBase<std::size_t, std::string, long> alphaBase;
std::cout << arthBase.getMax() << std::endl;
std::cout << alphaBase.getMax() << std::endl;
}