如何正确去除模板函数中的代码重复

问题描述 投票:0回答:3

我有这样的代码(类似“太空飞船”的运算符)。

template <class T>
int comparator(const T &a, const T &b){
    if (a < b){
        return -1;
    }else if (a > b){
        return +1;
    }

    return 0;
}

inline int comparator(const char *a, const char *b){
    return strcmp(a, b); // I never tried this, included just to get the idea
}

inline int comparator(char const a, char const b){
    return a - b;
}

inline int comparator(int const a, int const b){
    return a - b;
}

如何轻松删除几种有符号类型(char、short、int、long 等)的重复。我尝试过 SFINAE,但结果不太令人鼓舞。

c++ templates c++11 dry sfinae
3个回答
2
投票

您可以将重载、标签调度和模板混合在一起,如下例所示:

#include<type_traits>
#include<utility>
#include<iostream>

template <class T>
int comparator_(char, const T &a, const T &b){
    std::cout << "catch all" << std::endl;
    return (a<b)?-1:((a>b)?1:0);
}

template<typename T>
std::enable_if_t<std::is_same<T,int>::value or std::is_same<T,char>::value, int>
comparator_(int, T const a, T const b){
    std::cout << "char or int" << std::endl;
    return a - b;
}

template<typename... A>
int comparator(A&&... args) {
    return comparator_(0, std::forward<A>(args)...);
}

int main() {
    comparator(42,0);
    comparator('c', 'g');
    comparator(42u, 0u);
}

1
投票

首先将模板函数委托给模板类

template <class T>
int comparator(const T &a, const T &b){
    return comparator_impl<T>::comparator(a, b);
}

默认的模板类实现是您已经编写的:

template<class T>
class comparator_impl {

public:
    static int comparator(const T &a, const T &b){
        if (a < b){
           return -1;
        }else if (a > b){
           return +1;
        }

    return 0;
};

现在,有一个单独的模板类将用于有符号整数类型:

template<class T>
class signed_int_comparator_impl {

public:
    static int comparator(T a, T b)
    {
          return a-b;
    }
    return 0;
};

现在,特化第一个模板类,并从第二个模板类继承特化:

template<>
class comparator_impl<char> : public signed_int_comparator_impl<char> {};

template<>
class comparator_impl<int> : public signed_int_comparator_impl<int> {};

对于剩余的有符号整数类型,起泡沫,冲洗,重复。

如果您想专门针对

comparator_impl
进行
const char *
,请随意这样做。


1
投票
  1. 对于模板专业化,建议使用模板类/结构,而不是模板函数。请参阅 http://www.gotw.ca/publications/mill17.htm
  2. std::is_integral
    std::is_signed
    看起来像是适合您的 SFINAE 的正确工具。这是一个工作示例:https://ideone.com/8wm54h

#include <type_traits>
#include <utility>
#include <iostream>
 
template <typename T, typename = void>
struct Comparator
{
    int operator()(const T& a, const T& b) const
    {
        std::cout << "catch all\n";
        if (a < b)
        {
            return -1;
        }
        if (a > b)
        {
            return 1;
        }
        return 0;
    }
};
 
template <typename T>
struct Comparator<T, std::enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value>>
{
    int operator()(const T &a, const T &b) const
    {
        std::cout << "Signed arithmetic type\n";
        return a - b;
    }
};
 
template<typename T>
int comparator(const T& a, const T& b)
{
    return Comparator<T>()(a, b);
}
 
int main()
{
    comparator(42, 0);
    comparator('c', 'g');
    comparator(42u, 0u);
    comparator(42LL, 0LL);
    comparator(std::string("b"), std::string("a"));
}

  1. 使用 C++17,
    if constexpr
    使其更简单,无需 SFINAE 技巧(并且类型特征也更易于使用,使用
    _v
    https://gcc.godbolt.org/z/rY1h3jvsW

#include <type_traits>
#include <utility>
#include <iostream>
 
template<typename T>
int comparator(const T& a, const T& b)
{
    if constexpr (std::is_integral_v<T> && std::is_signed_v<T>)
    {
        std::cout << "Signed arithmetic type\n";
        return a - b;
    }
    else
    {
        std::cout << "catch all\n";
        if (a < b)
        {
            return -1;
        }
        if (a > b)
        {
            return 1;
        }
        return 0;
    }
}
 
int main()
{
    comparator(42, 0);
    comparator('c', 'g');
    comparator(42u, 0u);
    comparator(42LL, 0LL);
    comparator(std::string("b"), std::string("a"));
}

  1. 使用 C++20,我们可以使用函数重载的概念,特别是如果您更喜欢将函数分开而不是使用
    if constexpr
    的一个函数:https://gcc.godbolt.org/z/rEnMxxWPE

#include <type_traits>
#include <utility>
#include <iostream>

template<typename T>
int comparator(const T& a, const T& b)
{
    std::cout << "catch all\n";
    if (a < b)
    {
        return -1;
    }
    if (a > b)
    {
        return 1;
    }
    return 0;
}

template <typename T>
requires std::signed_integral<T>
int comparator(const T& a, const T& b)
{
    std::cout << "Signed arithmetic type\n";
    return a - b;
}

int main()
{
    comparator(42, 0);
    comparator('c', 'g');
    comparator(42u, 0u);
    comparator(42LL, 0LL);
    comparator(std::string("b"), std::string("a"));
}

int comparator(const std::signed_integral auto& a, const std::signed_integral auto& b)
{
    std::cout << "Signed arithmetic type\n";
    return a - b;
}

(在建议包含内联代码示例的评论后于 2024 年 9 月 30 日编辑。同时还添加了 C++17 和 C++20 版本)

© www.soinside.com 2019 - 2024. All rights reserved.