仅适用于特定模板类的模板

Enable template only for specific templated class

本文关键字:适用于      更新时间:2023-10-16

这个问题的灵感来自我以前的问题否模板参数扣除参数pack。

考虑以下代码示例:

#include <memory>
#include <string>
template<typename... FArgs>
class Callback
{
    public:
    class Handle{};
};
class BaseCallbackHandle
{
};
using TypeErasedCallbackHandle = std::unique_ptr<BaseCallbackHandle>;
template<typename H>
TypeErasedCallbackHandle makeTypeErasedCallbackHandle( H handle)
{
   return {};
}
int main()
{
    Callback<int>::Handle h;
    std::string s;
    makeTypeErasedCallbackHandle(h); //should compile fine
    makeTypeErasedCallbackHandle(s); //should raise a compile error
}

另请参见http://coliru.stacked-crooked.com/a/5f2a2e816eef6afd

功能模板makeTypeErasedCallbackHandle现在将任何类作为输入参数。有什么方法可以确保(例如,使用静态签名或enable_if),仅允许Callback<FArgs...>::Handle(使用任何FArgs)作为HCallback<int>::Handle的示例应编译,而std::string将失败。

定义Handle类中的类型,并在makeTypeErasedCallbackHandle()中参考该类型:

#include <memory>
#include <string>
template <typename... FArgs>
struct Callback {
    struct Handle {
        using callback_type = Callback<FArgs...>;    
    };
};
struct BaseCallbackHandle {
};
using TypeErasedCallbackHandle = std::unique_ptr<BaseCallbackHandle>;
template <typename H>
TypeErasedCallbackHandle makeTypeErasedCallbackHandle(H handle) {
    using callback_type = typename H::callback_type;
    return {};
}
int main() {
    Callback<int>::Handle h;
    std::string s;
    makeTypeErasedCallbackHandle(h); //should compile fine
    makeTypeErasedCallbackHandle(s); //should raise a compile error
}

实例示例

这将在任何不定定义嵌套类型的H实例化期间失败。


付出了更多的努力,您可以static_assert向客户提供有意义的消息,同时通过类型特征提高解决方案的灵活性。这具有一个优势,即callback_impl::is_callback可以专门用于任意手柄类型:

#include <memory>
#include <string>
namespace callback_impl {
struct callback_identification_type {};
template <typename T, typename = void>
struct is_callback : std::false_type {};
template <typename T>
struct is_callback<T,
    std::enable_if_t<std::is_same<typename T::callback_id_type,
                     callback_identification_type>::value>>
 : std::true_type {};
}
template <typename... FArgs>
struct Callback {
    struct Handle {
        using callback_id_type = callback_impl::callback_identification_type;    
    };
};
struct BaseCallbackHandle {
};
using TypeErasedCallbackHandle = std::unique_ptr<BaseCallbackHandle>;
template <typename H>
TypeErasedCallbackHandle makeTypeErasedCallbackHandle(H handle) {
    static_assert(callback_impl::is_callback<H>::value,
                  "The handle type is not a member of a recognised Callback<T...>");
    return {};
}
int main() {
    Callback<int>::Handle h;
    std::string s;
    makeTypeErasedCallbackHandle(h); //should compile fine
    makeTypeErasedCallbackHandle(s); //should raise a compile error
    return 0;
}

实例示例

输出:

g++ -std=c++14 -O2 -Wall -Wno-unused-local-typedefs -pedantic -pthread main.cpp && ./a.out
main.cpp: In instantiation of 'TypeErasedCallbackHandle makeTypeErasedCallbackHandle(H) [with H = std::__cxx11::basic_string<char>; TypeErasedCallbackHandle = std::unique_ptr<BaseCallbackHandle>]':
main.cpp:41:35:   required from here
main.cpp:32:5: error: static assertion failed: The handle type is not a member of a recognised Callback<T...>
     static_assert(callback_impl::is_callback<H>::value,
     ^~~~~~~~~~~~~

一种方法是传递一些额外的参数:

template <typename... Pack> struct packer {};
using TypeErasedCallbackHandle = std::unique_ptr<BaseCallbackHandle>;
template <typename... T>
TypeErasedCallbackHandle makeTypeErasedCallbackHandle(typename Callback<T...>::Handle h, T...)
{
   return {};
}
template <typename... T>
TypeErasedCallbackHandle makeTypeErasedCallbackHandle_2(typename Callback<T...>::Handle h, packer<T...>)
{
   return {};
}
int main()
{
    Callback<int>::Handle h;
    std::string s;
    makeTypeErasedCallbackHandle(h, 0); //should compile fine
    // OR
    makeTypeErasedCallbackHandle_2(h, packer<int>());
    //makeTypeErasedCallbackHandle(s); //should raise a compile error
}

这使用identity trick(由Stephan T. Lavavej)进行类型扣除。

您可以为您的班级家庭做虚构的成员,并将其称为替代方面:http://coliru.stacked-crooked.com/a/d5738766fd7ac45f

class Handle
{
    public:
    static void doNothing(){}
};

...

TypeErasedCallbackHandle makeTypeErasedCallbackHandle( H handle)
{
   H::doNothing();
   return {};

更标准的方法是使用诸如boost之类的库中使用班级特征或静态断言(可能已经是其中一些是语言的一部分)。

编辑:http://coliru.stacked-crooked.com/a/a/2a3adcb9d9dd274c这种方式稍好一些,而不是呼叫。

class BaseCallback
{
  ~BaseCallback() = 0;
};
template<typename... FArgs>
class Callback : public BaseCallback
{
  ~Callback(){...}
  ...
};
template<typename H>
TypeErasedCallbackHandle makeTypeErasedCallbackHandle( H handle)
{
  static_assert(std::is_base_of<BaseCallback, H>::value, "Must use a callback");
  return {};
}

this 应该工作,只允许您在回调上使用该功能