我正在尝试创建一种将所有运算符转发到其包含的对象的包装类,以尝试使其能够“假装”为包含的对象。我想写的代码看起来像这样(简化):
template<typename T>
class Wrapper
{
private:
T val;
public:
Wrapper(T value) : val(value) {}
auto operator++() -> decltype(++this->val)
{
return ++this->val;
}
};
这适用于 a int
,但如果我尝试将 a 传递给std::string
它,我会收到错误cannot increment value of type 'std::basic_string<char>'
。
我也尝试在这里使用 declval ,但这只会让事情变得更糟,因为它不仅仍然会在 上抛出错误,而且由于不是类std::string
,它还会在这种情况下抛出错误。int
int
现在,在正常情况下,根本不会生成这个函数,因为我没有调用它。但是,无论出于何种原因,decltype 仍在此函数上进行处理,即使它根本没有生成。(如果我删除 decltype 并将返回类型更改为 void,我可以std::string
毫无问题地编译。)
所以我的问题是:有什么办法可以解决这个问题吗?也许使用 SFINAE 的一些疯狂技巧?或者,这是否可能首先是编译器的不当行为,因为该函数没有生成代码?
编辑:解决方案,根据 BЈовић 建议的解决方案进行了一些修改:
//Class, supports operator++, get its declared return type
template<typename R, bool IsObj = boost::is_class<R>::value, bool hasOp = boost::has_pre_increment<R>::value> struct OpRet
{
typedef decltype(++std::declval<R>()) Ret;
};
//Not a class, but supports operator++, return type is R (i.e., ++int returns int)
template<typename R> struct OpRet<R, false, true>
{
typedef R Ret;
};
//Doesn't support operator++, return type is void
template<typename R> struct OpRet<R, true, false>
{
typedef void Ret;
};
template<typename R> struct OpRet<R, false, false>
{
typedef void Ret;
};
template<typename T>
class Wrapper
{
private:
T val;
public:
Wrapper(T value) : val(value) {}
auto operator++() -> typename OpRet<T>::Ret
{
return ++val;
}
};
这将适用于简单类型和类类型,对于类类型,也适用于 operator++ 的返回类型不是 R 的情况(这对于 operator++ 可能非常罕见,但为了最大程度的兼容性值得考虑。 )