使用 C 型变调器传递替身时出现意外的类型转换

Unexpected type conversion when using C-style variadics to pass doubles

提问人:user22544662 提问时间:9/12/2023 最后编辑:Jan Schultkeuser22544662 更新时间:9/12/2023 访问量:55

问:

我尝试了一个变量 Args。Matrix 类与 int 一起使用,但是当我使用 double 时,当我不转换它时,我得到了 23 的错误值。我读到其他类型的类型会自动转换

  • 浮点参数在浮点提升中转换为双精度 AS
  • Bool、Char、short 和 Unscoped 枚举被转换为 int 或更宽的整数类型,如整数提升“我不明白为什么我得到错误的值。
#include "matrix.h"
//#include "coordinate_sys.h"

int main(){

//Coordinate_3d<double>Coordinate_3(1,2,3);
//Coordinate_3.print_console();

Matrix<double>sick_matrix(2,2,4,20.5,(double)23,23,0);
sick_matrix.print_console();

while(1){}

return 0;
}
#ifndef MATRIX_H
#define MATRIX_H
#include <iostream>
#include <cstdarg>

template<typename T>
class Matrix{
private:
    T * arr_ptr;
    unsigned int rows;
    unsigned int cols;
    
public:
    Matrix(unsigned int rows, unsigned int cols, unsigned int args_ctr,...): rows(rows),cols(cols){
        //react to false input
        //--------------------------------------------------------------------------------------------
        if(cols * rows < args_ctr){ //to many args
            std::cout << "Matrix initilization with to many arguments" << std::endl;
            exit(EXIT_FAILURE);
        }
        //--------------------------------------------------------------------------------------------
        
        arr_ptr = new T[rows*cols];
        va_list args;
        unsigned int fill_var = 0;
        va_start(args,args_ctr);

        while (fill_var < args_ctr)
        {
            arr_ptr[fill_var] = va_arg(args,T);
            fill_var++;
        }
        
        va_end(args);   
    }

    void print_console(){for(unsigned int i = 0; i < rows*cols; i++){std::cout << arr_ptr[i] << std::endl;}}

};

#endif

我将使用变量模板参数来执行此操作,但为什么转换中会出现错误?

C++ 模板 variadic-functions 省略号

评论

1赞 Sam Varshavchik 9/12/2023
流行测验:和是什么类型?当然,显示的代码会期望它们是 s。是吗?230double
1赞 Remy Lebeau 9/12/2023
这正是为什么在C++中应该避免使用C型可调参数的原因。你失去了所有类型的安全。改用 C++ 样式的可变参数模板,或者在这种情况下也就足够了std::initializer_list

答:

3赞 Jan Schultke 9/12/2023 #1

问题在于,C 样式的可调参数绝对不会给你带来类型安全。 传递 a 和两个 s,并且您期望在构造函数中传递三个 s。(double)23,23,0doubleintdouble

您通常应该远离 C++ 中的 C 样式可变参数。

解决方案 A -std::initializer_list

Matrix(unsigned int rows, unsigned int cols, std::initializer_list<T> args)
  : rows(rows), cols(cols) {
    // args.size() can be used to obtain the number of arguments
}

解决方案 B - 静态大小的矩阵

在几乎所有情况下,您都知道编译时矩阵的大小。如果将行和列转换为非类型模板参数,则许多问题变得微不足道:

template <typename T, std::size_t Rows, std::size_t Columns = Rows>
class Matrix {
  private:
    // ...
  public:
    T data[Rows * Columns];
};

你甚至不再需要构造函数,可以这样写:

using Mat3f = Matrix<float, 3>; // convenience alias

Mat3f mat{/* ... */}; // aggregate initialization, no constructor needed

如果你坚持要有一个构造函数,它可以是一个只接受一个参数的构造函数,你仍然可以使用语法。std::initializer_list<T>Mat3f{...}