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

StackOverflow https://stackoverflow.com/questions/3295283

Вопрос

Я наткнулся на настоящего выжигателя мозгов в C ++, такого со мной раньше никогда не случалось.

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

Моя шаблонная функция объявлена следующим образом:

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

Это позже, в том же заголовке, определено следующим образом:

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

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

Примером забавных значений могли бы быть:0,0078125000000000000 и 2,104431116947e-317#DEN

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

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

Правка 1:

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

Правка 2:

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

Правка 3:

Истории из крипты C ++:

#include <sgt/matrix4.hpp>

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

    m0 *= m0;

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

    sgt::matrix4<float> m1(
        2, 0, 0, 1,
        0, 2, 0, 1,
        0, 0, 1, 0,
        0, 0, 0, 1);

    m1 *= m1;

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

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

В matrix4.hpp:

// ...

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

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

// ...

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

Правка 4:

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

Это было полезно?

Решение

Следующее не выполняется для моей in 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);
}

Выходной сигнал:

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);
}

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

ОК.Не исходя из моего прочтения стандарта, это должно работать так, как ожидалось:

Использование n2521
Раздел 14.7.1 Неявное создание экземпляра
Пункт 9

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

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

Пункт 11:

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

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

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

Другие советы

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

Я попробовал этот более простой код (на 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";
}

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

*Редактировать: По-видимому, проблема в компиляторе.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top