在仅在某些情况下使用decltype的模板中实例化函数定义

时间:2011-03-31 17:10:17

标签: c++ templates c++11 decltype

作为理解C ++ 0x的练习,我试图创建一个包含某些模板化类型指针的C ++类:

template <typename T>
class Wrapper {
    T *t;
    /* ... */
};

在Wrapper类中,我想公开T可以通过Wrapper类实现的任何重载操作符。包装器本身只是将函数调用转发给底层的t对象。

template <typename U>
auto operator+(U &u) -> decltype (*t + u) {
    return *t + u;
}

问题在于我不希望Wrapper暴露T可能无法实现的运营商。例如,如果T没有实现operator +,那么Wrapper也不应该公开operator +。

在operator +(和任何二进制操作)的情况下,一切都运行,因为运算符必然成为模板函数,因此只在我们尝试调用时实例化,例如,Wrapper :: operator +。

然而,在一元运算符(例如,++)的情况下,没有明确的方法来保护运算符,以便在tf实现operator ++时实例化它。例如,本类

中的operator ++的朴素实现
auto operator++() -> decltype(++(*t)) {
    return ++(*t);
}

无法为不支持operator ++()的T编译。

根据我对标准的理解,如果我们有以下使用Wrapper的代码

class X { };
Wrapper<X> w;

除非我们调用它(或显式地实例化它),否则我们将实例化Wrapper和Wrapper :: operator ++()的声明而不是它的定义。通常情况下这是可以的,因为X :: operator ++的使用仅出现在Wrapper :: operator ++()的定义中。但是,由于decltype,我们在声明中使用X :: operator ++,以便typechecker检查是否存在X :: operator ++,从而失败。

如果底层对象也支持operator ++(),我们可以定义operator ++()(通常是任何使用decltype的转发函数)和实例化它的属性吗?或者给出模板实例化和decltype的语义,这是不可能完成的?

3 个答案:

答案 0 :(得分:5)

您可以将运算符声明为非成员模板:

template <typename T>
auto operator++(Wrapper<T>& arg) -> decltype(++*arg.t) {
    return ++*arg.t;
}

答案 1 :(得分:4)

您也可以使用默认模板参数进行操作,只是为了使操作符的操作数相互依赖

template<typename Trick = T>
auto operator++() -> decltype(++(static_cast<Trick&>(*t))) {
    return ++(*t);
}

也许在

之间有辅助功能
template<typename /* Ignored */, typename T> T &&id(T &&t) {
    return std::forward<T>(t);
}

template<typename Void = void>
auto operator++() -> decltype(++(*id<Void>(t))) {
    return ++(*t);
}

答案 2 :(得分:2)

如果你能弄清楚如何在操作员签名中使用std::enable_if,这里有一个元函数来检查是否存在例如operator->#include <type_traits> template<typename T, typename R> inline R* has_deref_opr_sfinae_impl_helper(R (T::*)()) { return 0; } template<typename T, typename R> inline R* has_deref_opr_sfinae_impl_helper(R (T::*)() const) { return 0; } template< typename T, bool IsPointer = std::is_pointer<T>::value && !std::is_same< typename std::remove_cv< typename std::remove_pointer< typename std::remove_cv<T>::type >::type >::type, void >::value > class has_deref_opr { template< typename U, typename R = decltype(has_deref_opr_sfinae_impl_helper(&U::operator->)) > struct sfinae_impl { }; typedef char true_t; struct false_t { true_t f[2]; }; template<typename U> static true_t check(U*, sfinae_impl<U>* = 0); template<typename U> static false_t check(...); public: static bool const value = sizeof(check<T>(0)) == sizeof(true_t); }; template<typename T> class has_deref_opr<T, true> { public: static bool const value = true; };

has_deref_opr_sfinae_impl_helper

一些注意事项:

  • 我使用GC 4.4.1进行了测试,它不希望has_deref_opr位于{{1}}内,不确定原因。也许这在更新版本的GCC中有所改变
  • 由于模板实例化错误导致VC ++ 2010 SP1无法编译,我无法找到解决方法: - [

希望这有帮助。