【问题标题】:C++ SFINAE for group of methods and decorators用于方法组和装饰器的 C++ SFINAE
【发布时间】:2020-06-03 17:02:56
【问题描述】:

我有一个 std::variants 包含具有不同接口的对象。目标是如果变体中的对象具有某些方法,则调用它。 我正在尝试制作几个模板装饰器,并寻找一种方法来使用更少的样板和没有宏。 最后我的想法是这样的:

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&lt;Good&gt; 编译并选择模板专业化,View&lt;Bad&gt; 无法编译,因为 using T::b;View 中。 但它对 Good 和 Bad 类型都返回 true!

std::cout << IsFitToView<Good, View> << IsFitToView<Bad, View>;

我知道我可以通过单独检查来检查方法是否存在,并像这样检查

if constexpr(HasAFunc<T> && HasBFunc<T> && ...

但我必须创建许多不同的Views。它非常冗长且难以阅读。 请你解释一下为什么我的方法不起作用并给出任何想法来做我想做的事。 谢谢!

【问题讨论】:

  • View 正文,包括 using T::a;,在 IsFit 的直接上下文之外。
  • 或许可以定义a concept for each view
  • 听起来您是在尝试重塑 std::is_convertable 或在较小程度上重塑 std::is_invocable

标签: c++ templates c++17 sfinae


【解决方案1】:

你能解释一下为什么我的方法不起作用吗?

您当前使用 using 声明的方法失败,因为类(View 的)主体实例化发生在 immediate context 之外,这意味着它不会导致软错误,从而导致回退到IsFit 主模板,因此它的特化总是产生更好的匹配,导致IsFitToView 总是true

即使这样有效,using T::a; 也不会告诉您有关 a 的任何信息。它可以是单个函数、一组重载的函数、static 或非static 数据成员,甚至是恰好位于T 范围内的某种类型的别名。


你能给出任何想法来做我想做的事吗?

知道如何检查函数是否存在,您可以将视图定义为变量模板,对一般谓词进行分组,例如:

template <typename T>
inline constexpr bool ViewAB = HasAFunc<T> && HasBFunc<T>;

这样,您只需检查:

if constexpr (ViewAB<std::decay_t<decltype(obj)>>)

另一种解决方案是使用 detection idiom 表单库基础 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)>)

DEMO


或者,在 中,您可以将视图定义为概念:

template <typename T>
concept ViewAB = requires (T t)
{
    t.a(1);
    t.b(3.14f);
};

它不仅使代码更易于阅读,并通过示例使用清楚地提出要求,而且还会生成一条错误消息,说明不满足哪个约束。

DEMO 2

【讨论】:

    猜你喜欢
    • 2013-02-14
    • 2020-01-11
    • 2014-01-14
    • 2016-02-10
    • 2010-10-16
    • 2012-09-11
    • 2011-06-20
    • 2010-10-14
    • 2016-03-17
    相关资源
    最近更新 更多