Шаблонная функция C++ получает ошибочные значения по умолчанию

Я натолкнулся на реальную мозговую жару в C++, этого никогда не происходило со мной прежде.

Суть проблемы - то, что после вызова моего (шаблон) функционируют аргументы, для которых я определил значения по умолчанию, скремблировали их значения. Это только происходит, если я вызываю функцию со значениями по умолчанию.

Моя шаблонная функция объявляется как это:

template 
vector2 transform(vector2 const &vec, matrix4 const &m, T z = T(0), T w = T(1));

Это позже, в том же заголовке, определенном как это:

template 
inline vector2 transform(vector2 const &vec, matrix4 const &m, T z, T w)
{
 vector4 res = m * vector4(vec.x, vec.y, z, w);
 return vector2(res.x, res.y);
}

Теперь, когда я называю это со значениями по умолчанию (transform(vector2(0, 1), view_transform)) Я не получаю значения, которые я ожидаю. Продвижение в transform с VC ++ s отладчик я вижу z и w наличие "забавных" значений (то, которое, по моему опыту, означает что-то, не инициализируется правильно).

Пример забавные значения был бы: 0.0078125000000000000 и 2.104431116947e-317#DEN

Теперь я попытался найти ответ на C++ FAQ Облегченный, гугля его; даже попробованный для успокаивания меня с Schubert но я не могу ни за что в жизни понять это. Я предполагаю, что это действительно просто, и я подозреваю, что это - некоторое шаблонное дурачество на работе.

Существует ли способ получить значения по умолчанию, которые я ожидаю и хочу, и почему он делает это мне?

Редактирование 1:

Если я вызов изменения, таким образом, это использует плавания вместо этого (transform(vector2(0, 1), view_transform)) проблема уходит. Кажется, что это только происходит если T = double.

Редактирование 2:

Это только происходит, если у меня есть две специализации для double и float. Если я использую специализацию плавающую в одном месте, двойная специализация становится странной значения по умолчанию. Если я изменяю все места, функция вызвана так, она использует дважды проблемы, "уходит". Я все еще не понимаю, почему, хотя, это похоже, это использует дефектные смещения или что-то при установке z и w.

Редактирование 3:

Рассказы от склепа C++:

#include 

int main(int argc, char *argv[])
{
    sgt::matrix4 m0(
        2, 0, 0, 1,
        0, 2, 0, 1,
        0, 0, 1, 0,
        0, 0, 0, 1);

    m0 *= m0;

    sgt::vector2 blah0 = sgt::transform(sgt::vector2(1, 0), m0);

    sgt::matrix4 m1(
        2, 0, 0, 1,
        0, 2, 0, 1,
        0, 0, 1, 0,
        0, 0, 0, 1);

    m1 *= m1;

    sgt::vector2 blah1 = sgt::transform(sgt::vector2(1, 0), m1);

    printf("%f", blah0.x);
    printf("%f", blah1.x);
}

В matrix4.hpp:

// ...

template 
vector2 transform(vector2 const &vec, matrix4 const &m, T z = T(0), T w = T(1));

template 
inline vector2 transform(vector2 const &vec, matrix4 const &m, T z, T w)
{
    vector4 res = m * vector4(vec.x, vec.y, z, w);
    return vector2(res.x, res.y);
}

// ...

Если я выполняю это, двойная специализация имеет, это - корректные параметры по умолчанию, но версия плавающая получает обоих, которые это - параметры по умолчанию как нуль (0.000000), который, хотя лучше, это все еще, не является z = 0 и w = 1.

Редактирование 4:

Сделанный проблемой Подключения.

10
задан Skurmedel 21 July 2010 в 16:52
поделиться

3 ответа

У меня в Dev Studio не работает:

#include "stdafx.h"
#include <vector>
#include <iostream>

template <typename T>
std::vector<std::vector<T> > transform(std::vector<std::vector<T> > const &vec,
                                       std::vector<std::vector<std::vector<std::vector<T> > > > const &m,
                                       T z = T(0), T w = T(1));


template <typename T>
std::vector<std::vector<T> > transform(std::vector<std::vector<T> > const &vec,
                                       std::vector<std::vector<std::vector<std::vector<T> > > > const &m,
                                       T z, T w)
{
    std::cout << "Z" << z << "\n";
    std::cout << "W" << w << "\n";

    return vec;
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::vector<std::vector<int> >  xi;
    std::vector<std::vector<std::vector<std::vector<int> > > > mi;
    transform(xi,mi);

    std::vector<std::vector<float> >    xf;
    std::vector<std::vector<std::vector<std::vector<float> > > > mf;
    transform(xf,mf);

    std::vector<std::vector<double> >   xd;
    std::vector<std::vector<std::vector<std::vector<double> > > > md;
    transform(xd,md);
}

Output:

Z0
W1
Z0
W1.4013e-045
Z2.122e-314
W3.60689e-305

Так что я полагаю, что это не работает так, как ожидалось!!!

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

#include "stdafx.h"
#include <vector>
#include <iostream>

template <typename T>
std::vector<std::vector<T> > transform(std::vector<std::vector<T> > const &vec,
                                       std::vector<std::vector<std::vector<std::vector<T> > > > const &m
                                       T z = T(0), T w = T(1))
{
    std::cout << "Z" << z << "\n";
    std::cout << "W" << w << "\n";

    return vec;
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::vector<std::vector<int> >  xi;
    std::vector<std::vector<std::vector<std::vector<int> > > > mi;
    transform(xi,mi);

    std::vector<std::vector<float> >    xf;
    std::vector<std::vector<std::vector<std::vector<float> > > > mf;
    transform(xf,mf);

    std::vector<std::vector<double> >   xd;
    std::vector<std::vector<std::vector<std::vector<double> > > > md;
    transform(xd,md);
}

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

OK. Из моего чтения стандарта следует, что это должно работать как ожидалось:

Используя n2521
Раздел 14.7.1 Неявное инстанцирование
Параграф 9

Реализация не должна неявно инстанцировать шаблон функции, шаблон члена, невиртуальную функцию-член, класс-член или статический член данных шаблона класса, который не требует инстанцирования. Не определено, должна ли реализация неявно инстанцировать виртуальную функцию-член шаблона класса, если иначе эта виртуальная функция-член не была бы инстанцирована. Использование специализации шаблона в аргументе по умолчанию не должно приводить к неявному инстанцированию шаблона, за исключением того, что шаблон класса может быть инстанцирован, если его полный тип необходим для определения корректности аргумента по умолчанию. Использование аргумента по умолчанию в вызове функции приводит к неявному инстанцированию специализаций в аргументе по умолчанию.

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

Параграф 11:

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

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

Надеюсь, я правильно это интерпретировал :-)

.
5
ответ дан 4 December 2019 в 02:25
поделиться

Я не знаю, сработает ли это, но попробуйте использовать static_cast вместо преобразования стиля C для значений по умолчанию.

* Edit: Видимо проблема в компиляторе.

1
ответ дан 4 December 2019 в 02:25
поделиться

Оптимизирован ли код? Может быть, поэтому отладчик показывает неправильные значения.

Я пробовал этот более простой код (в g ++ 4.3.3), и он работает, как ожидалось.

template <typename T>
T increment(T a, T b = T(1))
{
    return a + b;
}

int main()
{
    double a = 5.0;
    std::cout << increment(a) << ", ";
    std::cout << increment(a, 3.0) << "\n";
}
2
ответ дан 4 December 2019 в 02:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: