Еще одна проблема с decltype - PullRequest
2 голосов
/ 03 мая 2011
//THIS IS JUST A FRAGMENT OF A static_numeric_limits.h for the purpose of this example   
 #include <limits.h>

    template<class T>
    struct static_numeric_limits;

    template<>
    struct static_numeric_limits<signed char>
    {/*min was outside of range for enum*/
        static const signed char min = SCHAR_MIN,
                                 max = SCHAR_MAX;
    };

    /*This "surplus" template is here for the reason that char is threated differently from signed char */
    template<>
    struct static_numeric_limits<char>
    {/*min was outside of range for enum*/
        static const char min = SCHAR_MIN,
                          max = SCHAR_MAX;
    };

    template<>
    struct static_numeric_limits<unsigned char>
    {
        static const unsigned char min = 0x0,
                             max = UCHAR_MAX;
    };
 ///REAL PROBLEM STARTS FROM HERE      
     template<class IntType,IntType low_range = static_numeric_limits<IntType>::min>
    struct Int
    {
        Int():value_(IntType())
        {}
        Int(const IntType& pattern)
        {
            value_ = (pattern);
        }
        constexpr inline IntType getValue()const
        {
            return value_;
        }
    private:
        IntType value_;
    };

    template<class IntType,class IntType_1>
    auto operator+
        (Int<IntType>& lhs, Int<IntType_1>& rhs)
        -> Int<decltype(lhs.getValue() + rhs.getValue())>//HERE IS THE PROBLEM
    {
        return lhs.getValue() + rhs.getValue();
    }

Ошибка (от VS2010) error C2027: use of undefined type 'static_numeric_limits<T>'
Ошибка (из gcc 4.6)
error: 'decltype ((lhs->getValue() + rhs->getValue()))' is not a valid type for a template constant parameter

Почему это не работает так, как я думал?

1 Ответ

1 голос
/ 04 мая 2011

Ошибка здесь в том, какой тип decltype выводит из вашего выражения; к сожалению, сообщения об ошибках не совсем ясны, и это действительно немного сложная проблема.

Рассмотрим тип выражения 0 + 0. Это int, да, но что более важно это значение (неофициально, это временно). Это означает, что decltype(0 + 0) - это не int, а int&&. Теперь учтите, что ваш код ничем не отличается в этом отношении: у вас все еще есть значение.

Проблема в том, что нетиповые параметры шаблона не могут быть ссылками на значения, поэтому у вас не может быть Int<int&&> из-за типа второго параметра. Что вы можете сделать, хотя это:

#include <type_traits>

// ...

template <class IntType, class IntType_1>
auto operator+(const Int<IntType>& lhs, // be const-correct!
                const Int<IntType_1>& rhs)
                -> Int<typename std::remove_reference<
                        decltype(lhs.getValue() + rhs.getValue())>::type>
{
    return lhs.getValue() + rhs.getValue();
}

Это снимает ссылку с int&&, давая вам голый тип int. Надеемся, что сообщение об ошибке в gcc имеет немного больше смысла: оно пытается сказать вам, что вы не можете использовать int&& для параметра не-типа.


Другая проблема, хотя, вероятно, и не является проблемой, заключается в том, что целочисленная арифметика претерпевает то, что называется обычными арифметическими преобразованиями . Таким образом, результат добавления значений двух Int<char> на самом деле будет int, поэтому ваш тип возврата должен быть Int<int> (и с фиксированным кодом).

Проблема в том, что вы не определили static_numeric_limits<int>. Но, как я уже сказал, я подозреваю, что это не проблема, и вы на самом деле определили это, просто не отображается в вашем вопросе.

...