Этот вопрос возникает из-за необходимости вызывать унаследованные подпрограммы C (FFTW), которые имеют разные имена функций в зависимости от типа (одинарная / двойная / четверная точность) в шаблонном классе C ++. Простой пример того, что я мог бы сделать, приведен в этом неправильном примере кода:

#include <cstdio>
#include <cmath>
#include <typeinfo>
#include <cstdlib>

void fcnd(double *x) {
    *x = pow(*x, 2);
    printf("%f\n", *x);
}

void fcnf(float *x) {
    *x -= 1;
    printf("%f\n", *x);
}

template <typename T> class Class {

public:
    void fcn() {
        T *var;
        if (typeid(T) == typeid(float)) {
            var = (float *) malloc(sizeof(float));
        } else if (typeid(T) == typeid(double)) {
            var = (double *) malloc(sizeof(double));
        }

        *var = 1.0;

        if (typeid(T) == typeid(float)) {
            fcnf(var);
        } else if (typeid(T) == typeid(double)) {
            fcnd(var);
        }
        free(var);
    }
};

int main() {
    Class<double> x;
    Class<float> y;

    x.fcn();
    y.fcn();
}

GCC жалобы:

test.cpp: In instantiation of 'void Class<T>::fcn() [with T = double]': test.cpp:42:11:   required from here test.cpp:22:17: error: cannot convert 'float*' to 'double*' in assignment
             var = (float *) malloc(sizeof(float));
                 ^ test.cpp:30:17: error: cannot convert 'double*' to 'float*' for argument '1' to 'void fcnf(float*)'
             fcnf(var);
                 ^ test.cpp: In instantiation of 'void Class<T>::fcn() [with T = float]': test.cpp:43:11:   required from here test.cpp:24:17: error: cannot convert 'double*' to 'float*' in assignment
             var = (double *) malloc(sizeof(double));
                 ^ test.cpp:32:17: error: cannot convert 'float*' to 'double*' for argument '1' to 'void fcnd(double*)'
             fcnd(var);
                 ^

Теперь я знаю, в чем здесь ошибка. Мой вопрос: почему C ++ не позволяет этого? Конечно, это безопасно, верно? Я думаю, что специализация может помочь здесь, это всегда правильный подход?

1
Thomas Anderson 27 Май 2017 в 21:42

2 ответа

Лучший ответ

Вызов функции на основе аргумента шаблона может быть выполнен следующим образом в C ++ 17:

template <typename T> class Class
{
public:
    void fcn()
    {
        // one line can do it
        T* var{ reinterpret_cast<T*>(std::malloc(sizeof(*var))) };

        *var = 1.0;

        // C++17's if constexpr does exactly what you need
        if constexpr (std::is_same_v<T, float>)
        {
            fcnf(var);
        }
        else if constexpr (std::is_same_v<T, double>)
        {
            fcnd(var);
        }

        free(var);
    }
};

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

auto set(float* f) noexcept
{
    return fcnf(f);
}

auto set(double* d) noexcept
{
    return fcnd(d);
}

template <typename T> class Class
{
public:
    void fcn() 
    {
        T* var{ reinterpret_cast<T*>(std::malloc(sizeof(*var))) };
        *var = 1.0;
        set(var); // there's a reason we have function overloading in C++
        free(var);
    }
};

Примечание. Я надеюсь, что в своем реальном коде вы проверите результат вызова std::malloc().

5
user2296177 27 Май 2017 в 19:06

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

void fcn(double& x) {
    fcnd(&x);
}

void fcn(float& x) {
    fcnf(&x);
}

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

#include <cstdio>
#include <cmath>
#include <iostream>

using namespace std;

/*
    Some C functions, actually defined elsewhere...
*/

void fcnd(double *x) {
    *x = pow(*x, 2);
}

void fcnf(float *x) {
    *x -= 1;
}

/*
    Some macros to simplify things
*/

#define define_overloaded_function_for(name, type, suffix)\
void name(type& x) { name##suffix(&x); }

#define define_overloaded_functions_for(name)\
define_overloaded_function_for(name, float, f)\
define_overloaded_function_for(name, double, d)\
/* define_overloaded_function_for(name, long double, ld) */

/*
    Define some functions
*/
define_overloaded_functions_for(fcn);
/* ...etc... */

int main(void) {
    float a = 705;
    double b = 2465;
    cout << "~ a ~" << endl;
    cout << a << endl;
    fcn(a);
    cout << a << endl;
    cout << "~ b ~" << endl;
    cout << b << endl;
    fcn(b);
    cout << b << endl;
}
0
Sir Galahad 27 Май 2017 в 20:44