仅为特定模板启用模板 class
Enable template only for specific templated class
这个问题的灵感来自于我之前的问题No template parameter deduction of parameter 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
现在接受任何 class 作为输入参数。有什么方法可以确保(例如使用静态断言或 enable_if),只有 Callback<FArgs...>::Handle
(任何 FArgs
)被允许作为 H
? Callback<int>::Handle
的示例将编译,而 std::string
将失败。
您可以为您的 class 家庭虚构成员,并在替代方面称呼它:
http://coliru.stacked-crooked.com/a/d5738766fd7ac45f
class Handle
{
public:
static void doNothing(){}
};
...
TypeErasedCallbackHandle makeTypeErasedCallbackHandle( H handle)
{
H::doNothing();
return {};
更标准的方法是使用 class 特征或静态断言,来自像 BOOST 这样的库(可能其中一些已经是语言的一部分)。
编辑:
http://coliru.stacked-crooked.com/a/2a3adcb9d9dd274c
这种方式稍微好一点,用var代替call。
一种方法是传递一些额外的参数:
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)进行类型推导。
在 Handle
class 中定义一个类型,并在 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,
^~~~~~~~~~~~~
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 {};
}
这个应该工作并且只允许你在回调中使用那个函数
这个问题的灵感来自于我之前的问题No template parameter deduction of parameter 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
现在接受任何 class 作为输入参数。有什么方法可以确保(例如使用静态断言或 enable_if),只有 Callback<FArgs...>::Handle
(任何 FArgs
)被允许作为 H
? Callback<int>::Handle
的示例将编译,而 std::string
将失败。
您可以为您的 class 家庭虚构成员,并在替代方面称呼它: http://coliru.stacked-crooked.com/a/d5738766fd7ac45f
class Handle
{
public:
static void doNothing(){}
};
...
TypeErasedCallbackHandle makeTypeErasedCallbackHandle( H handle)
{
H::doNothing();
return {};
更标准的方法是使用 class 特征或静态断言,来自像 BOOST 这样的库(可能其中一些已经是语言的一部分)。
编辑: http://coliru.stacked-crooked.com/a/2a3adcb9d9dd274c 这种方式稍微好一点,用var代替call。
一种方法是传递一些额外的参数:
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)进行类型推导。
在 Handle
class 中定义一个类型,并在 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,
^~~~~~~~~~~~~
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 {};
}
这个应该工作并且只允许你在回调中使用那个函数