仅适用于特定模板类的模板
Enable template only for specific templated class
这个问题的灵感来自我以前的问题否模板参数扣除参数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
)作为H
?Callback<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 应该工作,只允许您在回调上使用该功能
- FLTK 2.0构建和演示,适用于VS2019的2011年左右的代码库
- C++17 - 使用自定义分配器的节点提取/重新插入 - 适用于 clang++/libc++,但不适用于 libstd
- "string.h"在构建适用于iOS的qt应用程序中找不到消息
- 适用于 WebView2 旧版本的示例应用程序
- 在 NVIDIA GEFORCE GTX 1050 上下载适用于 Windows 10 的 openCL 1.2
- __attribute__(优化(0))) 是否适用于"recursively"?
- 为什么 std::erase(std::erase_if) 不是适用于<algorithm>任何容器的模板?
- 使用一个参数的模板函数时出错(适用于 2)
- 使用 适用于 Android 和 iOS 的 tf-lite C++ API
- 为什么这适用于 G++ 而不是 CLANG?
- 适用于 macOS 的 Xcode 应用程序。这就是我设置从USB麦克风输入获取音频的方式。一年前工作,现在没有了。为什么
- 适用于 Linux 的 c++ 上的代理脚本
- 为什么我的 SFINAE 表达式不再适用于 GCC 8.2?
- 使输出流式处理运算符适用于 boost::variant<std::vector<int>、int、double 的正确方法是什么>
- 有没有适用于Windows.lib文件的GNU二进制文件描述符(BFD)
- 模板函数仅适用于VS
- 如何在cmake中包含适用于g++或viceversa的库
- 适用于win32、linux、mac的POSIX C包装器
- WinDBG适用于从Visual Studio 2015保存的转储,但不适用于任务管理器。显示异常代码"not found"
- 从uint8_t到NPY_UINT16 PyArray_SimpleNewFromData.适用于Linux,但不适用于