【发布时间】:2013-02-13 04:43:04
【问题描述】:
我正在尝试创建一种将所有运算符转发到其包含对象的包装类,以尝试使其能够“假装”为包含对象。我喜欢写的代码看起来像这样(简化):
template<typename T>
class Wrapper
{
private:
T val;
public:
Wrapper(T value) : val(value) {}
auto operator++() -> decltype(++this->val)
{
return ++this->val;
}
};
这适用于 int,但如果我尝试将 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++ 来说可能非常罕见,但为了最大的兼容性。)
【问题讨论】:
-
如果它不处理所有的方法声明,它怎么知道它什么时候需要实例化一个方法定义?
-
我的假设是它不会检查 decltype 的参数,直到它真正开始为该方法生成代码。为什么需要?只是浪费周期获取不会使用的信息。
-
这个包装的意义何在?为什么不直接公开该成员?
-
如果让运算符返回
void,你会返回什么? -
@Pubby:也许这段代码是对错误的简单重现。我们鼓励人们不要向我们展示事情有多复杂。
标签: c++ templates c++11 sfinae decltype