我已经创建了一个类型安全的ID类,但是现在我想支持operator ++,如果底层类型也有它。从this和this回答我已经提出了两个替代方案,但是当它们用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; }
};
AId<>
如果operator++
也有AId<>::value_type
section {
height: 100%;
width: 100%;
display:inline-block;
background:red;
}
*{
padding:0px;
margin:0px;
}
怎么办?我只限于c ++ 11而且没有提升。
之前还有第二个问题,我自己提出了一个问题here。
虽然我实际上在我的代码中使用了@Sam Varshavchik answear,但我认为@Guillaume Racicot提供的更为通用,所以我选择了它作为解决方案。
我现在正在使用@ Barry的answear,这既简单又正确。
答案 0 :(得分:7)
我认为不需要SFINAE,或者根本不需要任何东西。
只需实施operator++
即可。如果底层类不支持它,并且没有为您的模板包装器调用operator++
,则操作符不会被实例化,不会造成任何伤害。
使用gcc 6.2.1进行测试,请注意mytemplate<not_incrementable>
将实例化,没有任何问题,只要没有任何尝试增加它:
#include <iostream>
#include <vector>
class not_incrementable {};
class incrementable {
public:
incrementable &operator++()
{
return *this;
}
};
template<typename T> class mytemplate {
public:
T t;
mytemplate<T> operator++()
{
++t;
return *this;
}
};
void foo()
{
mytemplate<not_incrementable> will_this_compile;
mytemplate<incrementable> will_this_compile2;
++will_this_compile2; // Compiles
// ++will_this_compile; // Will not compile
}
答案 1 :(得分:4)
正如其他答案中所述,实际上,如果你不使用它们,你可以留下没有实例化错误的函数。
但是,如果有人使用sfinae尝试检查你的班级是否支持operator++
,他的类型特征会给他误报,导致潜在的编译错误。如果您想支持该用例,那么您将面临很多选择。你需要有条件地实施它。
如果您想要成员的条件实现,您可以使用继承。
我们将把sfinae放在那个特性中并在以下后使用该特性:
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 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;
现在,我们可以在mixin中实现您班级的operator++
:
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); }
};
现在,为了有条件地实施operator++
功能,您可以将std::conditional
与我们的特性结合使用:
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 */
};
现在,由于只有当类型与类型特征匹配时才扩展mixin,所以如果operator++
具有增量运算符,则只获得TID
。如果没有,您最终会延长Dummy
,而不会实施运营商。
如果您想要有条件地实现复制和移动构造函数,那么这个技巧很不错。