用于方法和装饰器组的 C++ SFINAE
C++ SFINAE for group of methods and decorators
我有一个 std::variants 包含具有不同接口的对象。目标是如果 variant 中的对象有它,则调用一些方法。
我正在尝试制作几个模板装饰器,并寻找一种方法来使用更少的样板和没有宏。
最后我的想法是这样的:
class Good
{
public:
void a(int);
float b(float);
int c(double);
};
class Bad
{
public:
void a(int);
int c(double);
};
template<class T>
class View : protected T
{
public:
using T::a;
using T::b;
};
template<class T, template<class> class V>
constexpr bool IsFitToView = // ??
//usage
std::variant<Good, Bad> variant = //;
std::visit([](auto&& obj) {
if constexpr(IsFitToView<std::decay_t<decltype(obj)>, View>)
{
View view{obj}; // create view from obj
//here work with obj as view, calling a `a` and `b` methods;
}
}, variant);
主要问题是如何创建 IsFitToView 检查。我的做法是:
template<class T, template<class> class V, class = void>
struct IsFit : std::false_type
{};
template<class T, template<class> class V>
struct IsFit<T, V, std::void_t<V<T>>> : std::true_type
{};
template<class T, template<class> class V>
constexpr bool IsFitToView = IsFit<T, V>::value;
我曾希望它必须像 SFINAE 那样工作:View<Good>
编译并选择了模板专业化,View<Bad>
无法编译,因为 using T::b;
在 View
.
但它 returns 对于好类型和坏类型都是正确的!
std::cout << IsFitToView<Good, View> << IsFitToView<Bad, View>;
我知道我可以通过单独检查方法来检查方法是否存在,并像这样检查它
if constexpr(HasAFunc<T> && HasBFunc<T> && ...
但我必须创建许多不同的Views
。它非常冗长且难以阅读。
拜托,你能解释一下为什么我的方法不起作用,并给出任何想法来做我想做的事吗?
谢谢!
Could you explain why my approach doesn't work ?
您当前使用 using
声明的方法失败,因为 class' (View
's) 主体实例化发生在 immediate context 之外,这意味着它无法产生结果在一个软错误中会导致回退到 IsFit
主模板,因此它的专业化总是产生更好的匹配,导致 IsFitToView
总是 true
.
即使这有效,using T::a;
也不会告诉您有关 a
的任何信息。它可以是单个函数、一组重载的函数、一个 static
或非 static
数据成员,甚至是恰好存在于 T
中的某种类型的别名范围。
Could you give any ideas to do what I want?
了解如何检查函数是否存在,您可以将视图定义为变量模板,对一般谓词进行分组,例如:
template <typename T>
inline constexpr bool ViewAB = HasAFunc<T> && HasBFunc<T>;
这样,您只需检查:
if constexpr (ViewAB<std::decay_t<decltype(obj)>>)
另一个解决方案是使用 detection idiom 形式 Library fundamentals v2:
template <typename T>
using ViewAB = decltype(std::declval<T>().a(3), std::declval<T>().b(3.14f));
template <typename T>
using ViewC = decltype(std::declval<T>().c(2.7271));
并像这样使用它:
if constexpr (std::experimental::is_detected_v<ViewAB, decltype(obj)>)
或者,在 c++20 中,您可以将视图定义为概念:
template <typename T>
concept ViewAB = requires (T t)
{
t.a(1);
t.b(3.14f);
};
它不仅使代码更易于阅读并通过示例用法清楚地呈现需求,而且还会生成一条错误消息来解释未满足的约束条件。
我有一个 std::variants 包含具有不同接口的对象。目标是如果 variant 中的对象有它,则调用一些方法。 我正在尝试制作几个模板装饰器,并寻找一种方法来使用更少的样板和没有宏。 最后我的想法是这样的:
class Good
{
public:
void a(int);
float b(float);
int c(double);
};
class Bad
{
public:
void a(int);
int c(double);
};
template<class T>
class View : protected T
{
public:
using T::a;
using T::b;
};
template<class T, template<class> class V>
constexpr bool IsFitToView = // ??
//usage
std::variant<Good, Bad> variant = //;
std::visit([](auto&& obj) {
if constexpr(IsFitToView<std::decay_t<decltype(obj)>, View>)
{
View view{obj}; // create view from obj
//here work with obj as view, calling a `a` and `b` methods;
}
}, variant);
主要问题是如何创建 IsFitToView 检查。我的做法是:
template<class T, template<class> class V, class = void>
struct IsFit : std::false_type
{};
template<class T, template<class> class V>
struct IsFit<T, V, std::void_t<V<T>>> : std::true_type
{};
template<class T, template<class> class V>
constexpr bool IsFitToView = IsFit<T, V>::value;
我曾希望它必须像 SFINAE 那样工作:View<Good>
编译并选择了模板专业化,View<Bad>
无法编译,因为 using T::b;
在 View
.
但它 returns 对于好类型和坏类型都是正确的!
std::cout << IsFitToView<Good, View> << IsFitToView<Bad, View>;
我知道我可以通过单独检查方法来检查方法是否存在,并像这样检查它
if constexpr(HasAFunc<T> && HasBFunc<T> && ...
但我必须创建许多不同的Views
。它非常冗长且难以阅读。
拜托,你能解释一下为什么我的方法不起作用,并给出任何想法来做我想做的事吗?
谢谢!
Could you explain why my approach doesn't work ?
您当前使用 using
声明的方法失败,因为 class' (View
's) 主体实例化发生在 immediate context 之外,这意味着它无法产生结果在一个软错误中会导致回退到 IsFit
主模板,因此它的专业化总是产生更好的匹配,导致 IsFitToView
总是 true
.
即使这有效,using T::a;
也不会告诉您有关 a
的任何信息。它可以是单个函数、一组重载的函数、一个 static
或非 static
数据成员,甚至是恰好存在于 T
中的某种类型的别名范围。
Could you give any ideas to do what I want?
了解如何检查函数是否存在,您可以将视图定义为变量模板,对一般谓词进行分组,例如:
template <typename T>
inline constexpr bool ViewAB = HasAFunc<T> && HasBFunc<T>;
这样,您只需检查:
if constexpr (ViewAB<std::decay_t<decltype(obj)>>)
另一个解决方案是使用 detection idiom 形式 Library fundamentals v2:
template <typename T>
using ViewAB = decltype(std::declval<T>().a(3), std::declval<T>().b(3.14f));
template <typename T>
using ViewC = decltype(std::declval<T>().c(2.7271));
并像这样使用它:
if constexpr (std::experimental::is_detected_v<ViewAB, decltype(obj)>)
或者,在 c++20 中,您可以将视图定义为概念:
template <typename T>
concept ViewAB = requires (T t)
{
t.a(1);
t.b(3.14f);
};
它不仅使代码更易于阅读并通过示例用法清楚地呈现需求,而且还会生成一条错误消息来解释未满足的约束条件。