Использование черт в C ++ - PullRequest
5 голосов
/ 21 февраля 2010

Этот вопрос относится к моему последнему одному . Я пытаюсь решить проблему, используя traits<T> и traits<T*>. Пожалуйста, рассмотрите следующий код.

template<typename T>
struct traits
{
    typedef const T& const_reference;
};

template<typename T>
struct traits<T*>
{
    typedef const T const_reference;
};

template<typename T>
class test
{
public:   
    typedef typename traits<T>::const_reference const_reference;
    test() {}   
    const_reference value() const {
        return f;
    }
private:
    T f;
};

int main()
{
    const test<foo*> t;
    const foo* f = t.value(); // error here. cannot convert ‘const foo’ to ‘const foo*’ in initialization
    return 0;
}

Похоже, что компилятор не рассматривает специализацию признаков для указателей и принимает тип возврата value() как const foo вместо const foo*. Что я тут не так делаю?

Любая помощь будет отличной!

1 Ответ

2 голосов
/ 21 февраля 2010

Специализация используется. traits<foo*>::const_reference - это const foo. Если вы хотите, чтобы это был указатель, используйте:

template<typename T>
struct traits<T*>
{
    typedef const T* const_reference;
};

При этом traits<foo*>::const_reference будет const foo*.

Обратите внимание, что использование T в специализации traits<T*> полностью отделено от T в шаблоне traits. Вы можете переименовать его:

template<typename U>
struct traits<U*>
{
    typedef const U* const_reference;
};

и у вас будет та же специализация. Это имеет больше смысла, если у вас есть опыт в функциональном программировании.

Для начала представьте, что template <typename ...> представляет абстракцию, а не функция, абстрагирующая значение. Это как поворот

sum = 0
for item in [1,2,3]:
    sum += item

в:

function sum(l):
    sum = 0
    for item in l:
        sum += item
    return sum

, где l занимает место [1,2,3]. Мы можем вызвать sums из другой функции, которая сама имеет формальный параметр с именем l:

function sumsq(l):
    return sum(map(lambda x: x*x, l))

1034 * "l" не имеет ничего общего с sum "l".

С помощью шаблонов мы абстрагируем имена типов, а не значения. То есть мы переходим:

struct traits {
    typedef const double& const_reference;
};

в

template <typename T>
struct traits {
    typedef const T& const_reference;
};

Теперь рассмотрим не шаблонную специализацию:

template <>
struct traits<double*> {
    typedef const double* const_reference;
};

Здесь нет параметров шаблона для специализации, но вы можете думать о traits<double*> как о применении шаблона traits к double*. Извлеките double и получите:

template <typename T>
struct traits<T*> {
    typedef const T* const_reference;
};

Здесь T - это параметр для специализации, а не базовый шаблон.

...