为包含模板的 API 提供自定义点的首选方式是什么?

问题描述

假设我正在编写一个库,并且在某个时候,有一些代码想要对用户提供的类型的对象做一些特定的事情。以 swap 为例,虽然函数可以是任意的。什么是重要的:

  • 这应该是静态多态性(编译时决定)
  • 库可以提供一个认实现,即:
    • 适用于多种情况(适用于大多数类型)
    • OR 适用于所有类型,但可能对某些类型有更有效的实现
  • 库的用户应该能够替换这个认实现

在编译时在 C++ 中有两种主要的方法来做这样的事情:

  • 函数重载
  • 类模板特化

这些都不是理想的:

  • 函数重载不适用于用户定义类型是类模板的情况。函数模板不能部分专门化,拥有多个函数模板需要大量的维护工作。
  • 类模板处理重载完全没问题的琐碎情况。

因此,我的想法是 - 为什么不能两者兼而有之?事实上,一些 STL 功能提供了 2 种甚至更多的自定义行为方式。典型的STL算法提供3:

  • 提供将用作函子实现的额外函数参数(通常为 lambda)
  • 重载特定类型的特定运算符
  • 专门化属于标准库的特定类模板

在我的情况下,第一个选项(额外参数)是不可能的,因为假定的库不会立即使用该函子。

所以我有两种可能的方法来实现重载+特化:

// A

namespace lib {

template <typename T>
struct swapper // can be specialized
{
    void operator()(T& lhs,T& rhs) const noexcept
    {
        // default impl
    }
};

template <typename T>
void swap(T& lhs,T& rhs) noexcept // can be overloaded
{
    swapper<T>()(lhs,rhs);
}

template <typename T>
void core_function(/* ... */)
{
    T t1 = /* ... */;
    T t2 = /* ... */;

    /* ... */

    swap(t1,t2);
    
    /* ... */
}

}

// B

namespace lib {

template <typename T>
void swap(T& lhs,T& rhs) noexcept // can be overloaded
{
    // default impl
}

template <typename T>
struct swapper // can be specialized
{
    void operator()(T& lhs,T& rhs) const noexcept
    {
        swap(lhs,rhs);
    }
};

template <typename T>
void core_function(/* ... */)
{
    T t1 = /* ... */;
    T t2 = /* ... */;

    /* ... */

    swapper<T>()(t1,t2);
    
    /* ... */
}

}

// user code (the same for both)

namespace user {
    
struct user_type { /* ... */ };

// customization through overloading
void swap(user_type& lhs,user_type& rhs) noexcept
{
    // ...
}

// customization through class template specialization
template <>
struct swapper<user_type>
{
    void swap(user_type& lhs,user_type& rhs) const noexcept
    {
        // ...
    }
};

}

问题是 - 哪个实现更好 - A 还是 B? (函数,主类模板)调用哪个的顺序有什么区别吗?这两种实现方式各有优缺点吗?

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)