繁体   English   中英

如何使用类型特征来定义部分抽象的模板基类?

[英]How to use type traits to define a partially abstract template base class?

我正在研究以下想法:

存在具有多个模板参数的通常抽象的模板化基类。 此类定义了保证某些方法(即方法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参数合并到类模板参数列表中,或者是否可以在单独的模板中提供type trait参数参数列表。 在这种情况下,我这是不可能的,因为enable_if语句测试必须在此上下文中声明/有效的类模板参数。

万一您使用了真正复杂的类型特征魔术,那么对该部分进行更为详尽的评论将不胜感激。

这里烦人的部分是字符串版本需要访问成员变量。 因此,一种解决方法是将成员置于最基本的类中:

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>
{ };

经过一番修补和感谢一些想法,Barry在上面给了我他的职位,我自己又对这个问题采取了措施。 当Barrys的答案为实际问题提供解决方案时,我会去回答自己,并回答“您不愿意”。 为什么Barry和我在评论中讨论:

  1. 设计变得复杂
  2. 您会使用很多与实际项目无关的帮助程序类和结构来使类层次结构混乱,而这些辅助类和结构完全出于架构原因而存在,以使基于类型特征的方法选择起作用。

因此,解决此问题的方法更为清晰:将“ AbstractBase”类保持为纯抽象,并将类型特征与继承结合在一起,并使用类型特征来验证ArithmeticBase类和StringBase类,以验证它们仅采用有效的模板参数。 解决方案如下所示:

#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;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM