Создание определения функции в шаблоне, который использует decltype только при определенных обстоятельствах - PullRequest
6 голосов
/ 31 марта 2011

В качестве упражнения для понимания 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;
}

Подвох состоит в том, что я не хочу, чтобы операторы обертывания выставляли, которые T может не реализовать.Например, если T не реализует operator +, то Wrapper также не должен выставлять operator +.

В случае operator + (и любой двоичной операции) все работает, потому что оператор обязательно становится функцией шаблона и поэтомусоздается только тогда, когда мы пытаемся вызвать, например, Wrapper :: operator +.

Однако в случае унарных операторов (например, ++) нет четкого способа защитить оператор, чтобы онсоздается тогда, когда T реализует оператор ++.Например, наивная реализация оператора ++ в этом классе

auto operator++() -> decltype(++(*t)) {
    return ++(*t);
}

не компилируется для T, который не поддерживает оператор ++ ().

Из моего понимания стандарта, если мы имеемследующий код, который использует Wrapper

class X { };
Wrapper<X> w;

Мы создадим экземпляр Wrapper и объявление Wrapper :: operator ++ (), но не его определение , если мы не вызовем его (или явно создать его экземпляр).Обычно это было бы нормально, потому что использование X :: operator ++ происходит только в определении Wrapper :: operator ++ ().Однако из-за decltype мы используем X :: operator ++ в объявлении, так что проверка типов проверяет наличие X :: operator ++ и, таким образом, завершается неудачей.

Можем ли мы определить оператор ++ () (и вообще любойфункция пересылки, которая использует decltype) со свойством, для которого она создается, если базовый объект также поддерживает operator ++ ()?Или, учитывая семантику создания шаблона вместе с decltype, это невозможно сделать?

Ответы [ 3 ]

5 голосов
/ 31 марта 2011

Вы можете объявить оператор как не являющийся членом шаблона:

template <typename T>
auto operator++(Wrapper<T>& arg) -> decltype(++*arg.t) {
    return ++*arg.t;
}
4 голосов
/ 31 марта 2011

Вы также можете выполнять трюки с аргументами шаблона по умолчанию, просто чтобы сделать операнд оператора зависимым

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 голосов
/ 02 апреля 2011

Если вы можете понять, как работать с 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;
};

Несколько замечаний:

  • Я тестировал с GC 4.4.1, ему не нравилось, когда has_deref_opr_sfinae_impl_helper находится внутри has_deref_opr, не знаю почему.Возможно, это изменилось в более поздних версиях GCC
  • VC ++ 2010 SP1 не удается скомпилировать это из-за ошибки создания экземпляра шаблона, для которой я не смог найти обходной путь: - [

Надеюсь, это поможет.

...