提问人:NetoBF 提问时间:11/8/2023 最后编辑:NetoBF 更新时间:11/9/2023 访问量:70
SFINAE 用于根据函数的参数类型构造具有函数类型模板参数的类
SFINAE for constructing classes with function-type template parameter, based on the type of parameter for the function
问:
用一个朗朗上口的标题来总结这个话题并不容易。
我的问题如下: 我想调用create-function的不同实现,它为我返回一个指向被调用方的指针,该被调用方使用function-type-template-parameter进行实例化。必须根据 function-type-parameter-types 的返回类型和参数类型的type_traits来选择这些实现。
我有带有function-type-template-parameters的类(我希望这是该参数的正确表达式)。这些是虚拟类,它们可以有不同的实现,其中 call() 函数的实现方式不同。 因此,基类如下所示:
template <typename T> class Callee;
template <typename TReturn, typename... TParams> class Callee<TReturn(TParams...)>
{
public:
Callee() {}
virtual TReturn call(TParams... data) = 0;
};
创建的派生对象在某处注册,然后通过标识符传递指向创建的类的指针。
为了实现这一点并使其更容易用于开发人员,我创建了一个“Creator”类,它有一个静态成员函数,该函数调用“内部”函数来获取 Callee 实例。Callee<TReturn(TParams...)>*
所以现在我的观点是,我们需要根据传递的函数类型的参数类型来创建东西的不同实现。
我已经设法实施了参数包的类型是否包含引用的检查,但除了使用额外的 bool-template 参数外,我没有发现其他解决方案。这目前用于两者,也不是干净的解决方案。
所以现在它基本上看起来像这样:TReturn
TParams...
template <typename... TParams> class CallerCreator;
template <typename T, bool containsRef = false> static Callee<T>* createInternal(uint32_t id)
{
Callee<T>* callee
= reinterpret_cast<Callee<T>*>(searchCallee(id));
if (callee == nullptr)
{
callee = new OtherCallee<T, containsRef>(id);
}
return callee;
}
template <typename TReturn, typename... TParams>
class CallerCreator<TReturn(TParams...)> final
{
public:
static Callee<TReturn(TParams...)>* create(uint32_t id)
{
return createInternal<TReturn(TParams...), contains_reference<TParams...>::value || std::is_reference<TReturn>::value>(id);
}
};
为了完整起见,“contains_reference”实现(我们使用的是C++14...),它基于: 检查参数包是否包含类型
template <typename... List> struct contains_reference : std::true_type
{
};
template <typename Head, typename... Rest>
struct contains_reference<Head, Rest...>
: std::conditional<std::is_reference<Head>::value, std::true_type, contains_reference<Rest...>>::type
{
};
template <typename Tp> struct contains_reference<Tp> : std::false_type
{
};
我也为contains_non_trivially_copyable和contains_non_standard_layout这样做了。
正如您在代码片段中看到的,如果没有找到 Callee,则创建该代码段,我将 bool 转发到此类,因为根据类型是否包含引用,可以有不同的实现。
请参阅我用来“禁止”创建此内容的静态断言。
但这不是我想要的。我只希望 just 不分配这个对象。只需将实现视为完整性的附加信息,但它与问题无关。
想象一下:OtherCallee<T, containsRef>
OtherCallee
createInternal
OtherCallee
template <typename T, bool containsRef = false> class OtherCallee;
template <typename TReturn, typename... TParams>
class OtherCallee<TReturn(TParams...), false> : public Callee<TReturn(TParams...)>
{
public:
explicit OtherCallee(uint32_t id)
{
// static_assert(not contains_non_standard_layout<TParams...>::value, "You cannot use non-pod-types!");
// static_assert(not contains_non_trivially_copyable<TParams...>::value, "You cannot use non-pod-types!");
// static_assert(std::is_standard_layout<TReturn>::value, "You cannot use non-pod-types!");
// static_assert(std::is_trivially_copyable<TReturn>::value, "You cannot use non-pod-types!");
}
TReturn call(TParams... data) override
{ /* stuff done when no refs */ }
};
template <typename TReturn, typename... TParams>
class OtherCallee<TReturn(TParams...), true> : public Callee<TReturn(TParams...)>
{
public:
explicit OtherCallee(uint32_t id)
{
}
TReturn call(TParams... data) override
{ /*stuff done when refs (e.g. remove them) */}
}
再说一遍:想象一下所有其他专业。
我的问题是,我只是不想在里面创建任何这种情况。OtherCalllee
static_assert
不知何故,我需要根据参数类型对函数进行不同的实现。
所以另一个应该看起来像这样:createInternal
createInternal
template <typename T, bool containsRef = false /*, bool returnValIsPod */> static Callee<T>* createInternal(uint32_t id)
{
Callee<T>* callee
= reinterpret_cast<Callee<T>*>(searchCallee(id));
return callee;
}
我真的不知道我该如何做到这一点。我尝试直接传递 and 以使用完整的专用化,所以我可能会使用,但我不知道如何正确实现这一点,因为我看不到我如何区分是否以及 目标是我可以在 s 中发表评论,但它们永远不会抛出,因为在非 pod-type for 或的情况下不会调用构造函数的实现。TReturn
TParams...
enable_if
TReturn
void
TParams...
static_assert
TReturn
TParams...
我添加了一个抛出断言的最小示例:https://godbolt.org/z/47oedPsq7
也许它更容易,我只是目前不明白。
答:
使用 的重载。createInternal
template <typename T> static Callee<T>* createInternal(
std::false_type containsRef,
uint32_t id
)
{
Callee<T>* callee
= reinterpret_cast<Callee<T>*>(searchCallee(id));
if (callee == nullptr) {
callee = new OtherCallee<T, containsRef>(id);
}
return callee;
}
template <typename T> static Callee<T>* createInternal(
std::true_type containsRef,
uint32_t id
)
{
Callee<T>* callee
= reinterpret_cast<Callee<T>*>(searchCallee(id));
if (callee == nullptr) {
// callee = new OtherCallee<T, containsRef>(id);
// whatever you do when containsRef is true
}
return callee;
}
然后在创建时:
template <typename TReturn, typename... TParams>
class CallerCreator<TReturn(TParams...)> final {
public:
static Callee<TReturn(TParams...)>* create(uint32_t id) {
std::integral_constant<bool, contains_reference<TParams...>::value || std::is_reference<TReturn>::value>(id)> containsRef;
return createInternal<TReturn(TParams...)>(containsRef, id);
}
};
模板函数不支持模板部分专用化,但支持重载。
因此,我们将 填充到函数参数中并传入。调用每个重载或匹配项。containsRef
std::true_type
std::false_type
containsRef
(std::true_type
是 的别名。std::integral_constant<bool, true>
如果您以后需要将其传递给非类型模板参数,则可以将其用作编译时值,就像我上面所做的那样,但我怀疑您不必在这里使用。std::integral_constant
...
您还可以执行另一个技巧,基本上是 向后移植 ,您可以在其中实例化并运行基于或参数的两个 lambda 之一。if constexpr
true_type
false_type
但你可能不需要这种技术。
评论
if constexpr
createInternal
static constexpr bool
Callee<T>
createInternal
createInternal<TReturn(TParams...), contains_reference<TParams...>::value || std::is_reference<TReturn>::value>(id)