SFINAE只有在可能的情况下才有一个类成员 [英] SFINAE to have a class member only if possible

查看:312
本文介绍了SFINAE只有在可能的情况下才有一个类成员的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经做了一个类型安全的ID类,但现在我想支持运算符++如果底层类型也有它。从此< a> answears我想出了两个替代品,但他们都失败,当实例化与AId:

  template< typename T,typename TID = unsigned int> 
struct AId {
typedef AId< T,TID>类型;
typedef T handled_type;
typedef TID value_type;

private:
value_type id;

template< typename _T> struct IsIncrementable
{
template< typename _U>使用rm_ref = typename std :: remove_reference< _U> :: type;
typedef char(& yes)[1];
typedef char(& no)[2];
template< class _U>
static yes test(_U * data,typename std :: enable_if<
std :: is_same< _U,rm_ref< decltype(++(* data))> :: value
> :: type * = 0);
static no test(...);
static const bool value = sizeof(yes)== sizeof(test((rm_ref <_T> *)0));
};

public:
explicit AId(const value_type& id):id(id){}

...

/ /这失败,出现错误:没有匹配operator ++(操作数类型是
//'AId< some_type,std :: basic_string< char>> :: value_type
// {aka std :: basic_string< char>}')
// auto operator ++() - > decltype(++ id,std :: ref(type())){++ id; return * this; }
// ^
template< typename = decltype(++ id)>
auto operator ++() - > decltype(++ id,std :: ref(type())){++ id; return * this; }

//错误:在'struct std :: enable_if< false,int>'
模板中没有名为'type'的类型< typename std :: enable_if< IsIncrementable< value_type> value,int> :: type = 0>
类型operator ++(int / * postfix * /){type old(id); ++ id; return old; }
};

如何 AId<> 只有当 AId<> :: value_type 也有它时,code> operator ++



以前有一个第二个问题,我已经从中提出了一个问题这里



虽然我实际上在我的代码中使用@Sam Varshavchik answear,我认为由@Guillaume Racicot提供的更一般,所以我选择作为解决方案。



我现在使用@ Barry的 answear 简单和正确。

解决方案

正如在其他答案中所说,确实,你可以离开函数,但是,如果有人使用sfinae尝试检查你的类是否支持 operator ++ ,他的类型性状会给他假阳性,导致潜在的编译错误。如果你想支持那个用例,你就没有很多选择。



如果你想要成员的条件实现,你可以使用继承。



我们将sfinae放在该trait中,并在以下位置使用该trait:

  template< typename,typename> 
struct has_increment:std :: false_type {};

template< typename T>
struct has_increment< T,void_t< decltype(++ std :: declval< T>())> :std :: true_type {};

类型 void_t (与C ++ 11兼容):

  //使用结构实现的void_t对c ++ 11编译器更好$ b template< typename ...> 
struct voider {using type = void; };

template< typename ... ts>
using void_t = typename voider< Ts ...> :: type;

现在,我们可以实现 operator ++ 您的类在mixin中:

 模板< typename Child> 
struct MixinIncrement {
auto operator ++(int / * postfix * /){
Child :: type old(self()。id);
++(self()。id);
return old;
}

private:
const Child& self()const {return * static_cast< const Child *>(this); }
Child& self(){return * static_cast< Child *>(this); }
};

现在,为了有条件地实现 operator ++ ,您可以使用我们的trait使用 std :: conditional

  struct假 {}; 

template< typename Child>
using Parent = typename std :: conditional< has_increment< TID> :: value,MixinIncrement< Child> Dummy>

template< typename T,typename TID = unsigned int>
struct AId:Parent< AId< T,TID>> {
/ * your stuff * /
};

现在,由于只有当类型匹配类型trait时才扩展mixin, operator ++ 如果 TID 有递增运算符。如果没有,你最终会扩展 Dummy



nice如果你想有条件地实现复制和移动构造函数。


I have made a type-safe ID class, but now I want to support operator++ if the underlying type also has it. From this and this answears I have come up with 2 alternatives, but they both fail when instantiated with AId:

template<typename T, typename TID = unsigned int>
struct AId {
  typedef AId<T, TID> type;
  typedef T handled_type;
  typedef TID value_type;

private:
  value_type id;

  template<typename _T> struct IsIncrementable
  {
    template<typename _U> using rm_ref = typename std::remove_reference<_U>::type;
    typedef char (&yes)[1];
    typedef char (&no)[2];
    template<class _U>
    static yes test(_U *data, typename std::enable_if<
                      std::is_same<_U, rm_ref<decltype(++(*data))>>::value
                    >::type * = 0);
    static no test(...);
    static const bool value = sizeof(yes) == sizeof(test((rm_ref<_T> *)0));
  };

public:
  explicit AId(const value_type &id) : id(id) {}

...

  //This fails with error: no match for 'operator++' (operand type is
  //'AId<some_type, std::basic_string<char> >::value_type
  //{aka std::basic_string<char>}')
  //auto operator++() -> decltype(++id, std::ref(type())) { ++id; return *this; }
  //                              ^
  template<typename = decltype(++id)>
  auto operator++() -> decltype(++id, std::ref(type())) { ++id; return *this; }

  //error: no type named 'type' in 'struct std::enable_if<false, int>'
  template<typename std::enable_if<IsIncrementable<value_type>::value, int>::type = 0>
  type operator++(int /*postfix*/) { type old(id); ++id; return old; }
};

How can AId<> have operator++ only if AId<>::value_type also has it? I'm limited to c++11 and no boost.

Previously there was a second question from which I have made a question on its own here.

Although I'm actually using @Sam Varshavchik answear in my code, I consider the one provided by @Guillaume Racicot to be more general, so I chose that as a solution.

I'm now using @Barry's answear which is both simple and correct.

解决方案

As stated in other answer, indeed, you can left function that have errors not instantiated if you don't use them.

However, if someone use sfinae to try to check if your class supports operator++, his type trait will give him false positive, causing potential compilation errors. If you want to support that use case, you are left with not a lot of choices. You need to implement it conditionally.

If you want conditional implementation of a member, you can use inheritance.

We will put sfinae in that trait and use that trait after:

template<typename, typename>
struct has_increment : std::false_type {};

template<typename T>
struct has_increment<T, void_t<decltype(++std::declval<T>())>> : std::true_type {};

The type void_t can be implemented like this (with C++11 compatibility):

// void_t implemented with a struct works better for c++11 compilers
template<typename...>
struct voider { using type = void; };

template<typename... Ts>
using void_t = typename voider<Ts...>::type;

Now, we can implement the operator++ of your class in a mixin:

template<typename Child>
struct MixinIncrement {
    auto operator++(int /*postfix*/) {
        Child::type old(self().id);
        ++(self().id);
        return old;
    }

private:
    const Child& self() const { return *static_cast<const Child*>(this); }
    Child& self() { return *static_cast<Child*>(this); }
};

Now, to conditionally implement the operator++ function, you can use std::conditional with our trait:

struct Dummy {};

template<typename Child>
using Parent = typename std::conditional<has_increment<TID>::value, MixinIncrement<Child>, Dummy>::type;

template<typename T, typename TID = unsigned int>
struct AId : Parent<AId<T, TID>> {
    /* your stuff */
};

Now, since you extends the mixin only if the type is matching the type trait, you only get the operator++ if TID has the increment operator. You end up extending Dummy if not, which don't have the operator implemented.

This very same trick is nice if you want to conditionally implement copy and move constructors.

这篇关于SFINAE只有在可能的情况下才有一个类成员的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆