如何创建一个类型特征来避免编写冗余的特化实现?

4
我有一个类模板,其中包含一个主模板,旨在与任何类型参数 T 一起使用。然而,由于某些特殊需求,我需要像这样使用模板特化:
template<typename T>
class foo 
{
private:
    T result;
public:
    void modify_result(T a)
    {
        result = some_operations(a);
    }
};

template<>
class foo<uint64_t> 
{
private:
    uint64_t result;
public:
    // notice: uint32_t not the same as our template argument
    void modify_result(uint32_t a)
    {
        result = some_operations(a);
    }
};

问题是我必须为每个特定情况创建大量的完全专业化。如果我想修改或添加类的内容,我必须对每个专业化进行操作,这对于可维护性来说真的很糟糕。
我希望有一种类型特征,可以检查T的类型。如果T是某种类型,例如uint64_t,那么我们就知道方法的输入或方法内部的某些变量需要是uint32_t类型。这将使我能够自定义模板而无需额外的维护成本。
3个回答

4
你可以定义一个类型特性,并为每个特定的类型添加专门化,例如:
template <typename T>
struct parameter_type {
    using type = T;
};

template <>
struct parameter_type<uint64_t> {
    using type = uint32_t;
};

然后使用它:

template<typename T>
class foo {
private:
    T result;
public:
    void modify_result(typename parameter_type<T>::type a) {
        result = some_operations(a);
    }
};

3
注意:为了避免在各处编写typename parameter_type<T>::type,您还可以在类中声明一个类型别名using parameter = typename parameter_type<T>::type;,或者自C++20起,只需声明using parameter = parameter_type<T>::type; - Jan Schultke
3
注意:为了避免在每个地方都写typename parameter_type<T>::type,你还可以在类中声明一个类型别名using parameter = typename parameter_type<T>::type;,或者自C++20以来只需写using parameter = parameter_type<T>::type; - Jan Schultke

2
在C++17中,我建议使用if constexpr,分别检查您希望单独处理的每种类型。通过这样做,您将能够在一个地方看到不同类型的实现,从而(可以说是)更容易维护代码。
template<typename T> class foo 
{
private:
   T result;
public:

   template<typename U>
   void modify_result(U a) 
   {
       if constexpr (std::is_same_v<U, uint64_t>)
       {
           // ... do something special for uint64_t type 'a'.
           result = some_operations(a);
       }
       else if constexpr (std::is_same_v<U, std::string>)
       {
           // ... do something special for std::string type 'a'.
           result = some_operations(a);
       }
       else
       {
           // ... all other types cases!
       }
   } 
};

1
其他答案建议使用类型特征constexpr if。第三个选项是将所有依赖于模板参数的内容,仅限于那部分,重构到一个基类中:
template<typename T>
class foo_base {
private:
    T result;
public:
    void modify_result(T a) {
        result = some_operations(a);
    }
};

template<>
class foo<uint64_t> {
private:
    uint64_t result;
public:
    void modify_result(uint32_t a) {
        result = some_operations(a);
    }
};

template <typename T>
class foo : public foo_base {
    // anything that does not depend on T is here
};

这样,您只需要编写一次 foo 并可以专门为 foo_base 进行定制。在三种选择中,我会选择类型特征。当要专门化的部分仅在有限范围内时,constexpr if 很好用,并且继承并不总是正确的工具(说得不够准确,当您实际上只想更改一个实现细节时,它会更改您的类型是什么。但有时您确实想更改类型是什么...)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接