すべての演算子を含まれているオブジェクトに転送する一種のラッパークラスを作成して、含まれているオブジェクトに「なりすます」ことができるようにしようとしています。私が書きたいコードは次のようになります(簡略化):
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を削除し、return typeを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 ++ではおそらく非常にまれですが、互換性を最大にするために考慮する価値があります。 )。