【问题标题】:SFINAE enable/disable function and template aliasSFINAE 启用/禁用功能和模板别名
【发布时间】:2015-05-26 12:29:18
【问题描述】:

我想知道如何实现我在下面描述的内容。
考虑一个基础 CRTP 类,其中一个功能需要启用而另一个功能需要禁用。
这是通过取决于类型定义的特征类控制的。
我最好不想在我的特征类中定义任何东西,但如果它有助于将其定义为 void 我可以接受。 cmets 中的更多信息,因为在那里显示我想要实现的目标要容易得多。

template<class T>
struct Model_Traits;

// Base CTRP class
template<typename Model>
class BaseModel
{
  inline const Model& impl() const { return static_cast<Model const&>(*this); }

  // not sure how to enable this template or not based on my traits class
  // which is necessary as some wont have it defined
  using foo_t = typename Model_Traits<Model>::foo_t;

  // this one should be enabled when foo_t is not defined
  void optimize(const foo1& f1, const foo2& f2, foo3* f3)
  {
    impl().optimize(f1,f2,f3);
  }

  // this one should only be enabled if foo_t is defined
  // not sure if this is correct
  template<typename T = foo_t,
           typename = std::enable_if<foo_t>::type>
  void optimize(const foo1& f1, const foo2& f2, foo3* f3, foo_t* f4)
  {
    impl().optimize(f1,f2,f3, f4);
  }
}

// First class defining the foo_t
template<MyModel>
struct Model_Traits
{
   using foo_t = myFoo;
}

class MyModel : public BaseModel<MyModel>
{
   void optimize(const foo1& f1, const foo2& f2, foo3* f3, foo_t* f4);
}

// Second class not defining foo_t
template<MyModel2>
struct Model_Traits
{

}

class MyModel2 : public BaseModel<MyModel2>
{
   void optimize(const foo1& f1, const foo2& f2, foo3* f3);
}

我当然应该说这是简化的,我的代码看起来不像那样,但如果你把所有其他的东西都去掉,它就很接近了。

有什么想法吗?

【问题讨论】:

  • 也许您可以使用“CppCon 2014:Walter E. Brown”现代模板元编程:纲要中的 void_t 技巧。此处显示了如何使用它来实现概念的示例:@987654321 @ 基本上,它是一种根据是否定义某些内容来重载模板的方法。

标签: c++ c++11 sfinae


【解决方案1】:

我们可以通过几个步骤做到这一点。首先,为foo_t 是否使用void_t 定义一个类型特征:

template <typename...>
using void_t = void;

template <typename T, typename = void>
struct has_foo_t : std::false_type { };

template <typename T>
struct has_foo_t<T, void_t<typename T::foo_t>> : std::true_type { };

然后我们将使用它来将我们的 optimize 函数转发到 SFINAE 友好的上下文中:

template <typename Model>
class BaseModel {
public:
    template <typename... Args>
    void optimize(Args&&... args) {
        optimize_impl<Model>(std::forward<Args>(args)...);
    }
};

我们将有两个impl 函数:一个enabled 和一个disabled:

template <typename T,
          typename = std::enable_if_t<has_foo_t<T>::value>>
void optimize_impl(const foo1& f1, const foo2& f2, foo3* f3, typename T::foo_t* f4)
{ .. }

template <typename T,
          typename = std::enable_if_t<!has_foo_t<T>::value>>
void optimize_impl(const foo1& f1, const foo2& f2, foo3* f3)
{ .. }

即使在这种情况下TModel,我们仍然需要它作为函数模板参数,以允许替换失败是“软”失败而不是硬编译错误。

【讨论】:

  • 我认为不需要将这里的参数完美地转发给 _impl 函数。我错过了什么吗?
  • @xerion 今天,他们采用原始指针。明天,谁知道呢?为什么完善他们?
【解决方案2】:

您的代码中存在概念错误,因为您将类型输入std::enable_if 类的第一个参数(而它应该是布尔值):

typename = std::enable_if<foo_t>::type
  ...

(另外,std::enable_if前面少了一个typename

因此,您的特征类应该包含一个static constexpr bool,而不是一个类型,它表示是否应该启用该功能:

static constexpr bool enable_foo = Model_Traits<Model>::enable_foo;

然后您可以将其传递给std::enable_if(或更方便:std::enable_if_t——该别名让您忘记typename::type)。


从`std::enable_if`派生

另一个直接的解决方法是从std::enable_if 派生您的特征类:

template<T>
struct Model_Traits : public std::enable_if<false> {}

template<>
struct Model_Traits<MyModel> : public std::enable_if<true, myFoo> {}

template<>
struct Model_Traits<MyModel2> : public std::enable_if<false>
{
    //in principle you don't need to restate that, just for clarity
}

然后通过

  template<typename T = foo_t,
           typename = typename Model_Traits<T>::type>
  void optimize(const foo1& f1, const foo2& f2, foo3* f3, foo_t* f4)
  {
      impl().optimize(f1,f2,f3, f4);
  }

这将只为MyModel启用该功能,而不是MyModel2


使用`void_t`

此外,正如评论中提到的,在更现代的风格中,您还可以使用void_t

template<class ...> using void_t = void;

struct A {};

struct B
{
    using foo_t=double;    
};

struct C
{
    template<typename T, typename = void_t<typename T::foo_t> >
    void foo(T const& t)
    {
        std::cout<<"hello"<<std::endl;
    }
};

int main()
{
    //C{}.foo(A{});  //compile time-error
    C{}.foo(B{});    //ok, prints "hello"
}

DEMO

这可能是最直接的选择。另见some related threads on SO

【讨论】:

  • 所有答案实际上都非常有帮助,因为我能够结合一些想法并想出类似的东西。最后,因为我有另一个为同一个函数定义的可选参数,所以我选择添加一个“布尔”变量来控制 SFINAE using has_extra_types = std::true_type; using foo_t = type1; using foo2_t = type2;
【解决方案3】:

你可以创造你的特质:

namespace detail
{
    template <typename T>
    decltype(std::declval<typename T::foo_t>(), std::true_type{}) has_foo_t_impl(int);

    template <typename T>
    std::false_type has_foo_t_impl(...);
}

template <typename T>
using has_foo_t = decltype(detail::has_foo_t_impl<T>(0));

然后创建一个辅助类:

template<typename Model, bool = has_foo_t<Model>>
struct BaseModelOptimizeHelper;

template<typename Model>
struct BaseModelOptimizeHelper<Model, true>
{
    void optimize_impl(const Model& model, const foo1& f1, const foo2& f2, foo3* f3, foo_t* f4){
        model.optimize(f1,f2,f3, f4);
    }
};

template<typename Model>
struct BaseModelOptimizeHelper<Model, false>
{
    void optimize_impl(const Model& model, const foo1& f1, const foo2& f2, foo3* f3)
    {
        model.optimize(f1,f2,f3);
    }
};

最后:

template<typename Model>
class BaseModel : protected BaseModelOptimizeHelper<Model>
{
    const Model& impl() const { return static_cast<Model const&>(*this); }
public:
    template <typename ... Ts>
    void optimize(Ts&&... ts) { optimize_impl(impl(), std::forward<Ts>(ts)...); };

};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多