2010-07-20 31 views
10

Tôi đã đánh trúng một bộ não thật sự trong C++, nó chưa bao giờ xảy ra với tôi trước đây.Chức năng mẫu C++ nhận giá trị mặc định không chính xác

Ý chính của vấn đề là khi yêu cầu hàm (mẫu) của tôi, các đối số mà tôi đã xác định mặc định có giá trị của chúng bị xáo trộn. Nó chỉ xảy ra nếu tôi gọi hàm với giá trị mặc định.

hàm mẫu của tôi được khai báo như thế này:

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

Nó được sau đó, trong cùng một tiêu đề, định nghĩa như thế này:

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

Bây giờ khi tôi gọi đây với giá trị mặc định (transform(vector2<double>(0, 1), view_transform)) Tôi không nhận được các giá trị tôi mong đợi. Bước vào transform với trình gỡ lỗi VC++ s Tôi thấy zw có các giá trị "vui" (theo kinh nghiệm của tôi có nghĩa là một cái gì đó không được khởi tạo đúng cách).

Ví dụ giá trị hài hước sẽ là: 0,0078125000000000000 và 2.104431116947e-317 # DEN

Giờ đây tôi đã cố gắng tìm câu trả lời về C++ Hỏi đáp Lite, googling nó; thậm chí cố gắng bình tĩnh với Schubert, nhưng tôi không thể cho cuộc sống của tôi tìm ra nó. Tôi đoán nó thực sự đơn giản và tôi nghi ngờ đó là một số loại tomfoolery mẫu tại nơi làm việc.

Có cách nào để có được các giá trị mặc định mà tôi mong đợi và muốn không, và tại sao nó lại làm điều này với tôi?

Sửa 1:

Nếu tôi gọi đổi để nó sử dụng phao nổi thay vì (transform(vector2<float>(0, 1), view_transform)) vấn đề đi xa. Có vẻ như điều này chỉ xảy ra nếu T = double.

Chỉnh sửa 2:

Nó chỉ xảy ra nếu tôi có hai chuyên ngành cho doublefloat. Nếu tôi sử dụng một chuyên ngành nổi ở một nơi thì chuyên môn hóa kép sẽ có giá trị mặc định lạ. Nếu tôi thay đổi tất cả các địa điểm mà hàm được gọi để nó sử dụng gấp đôi các vấn đề "biến mất". Tôi vẫn không hiểu tại sao mặc dù, nó giống như nó sử dụng bù đắp sai sót hoặc một cái gì đó khi thiết lập zw.

Sửa 3:

Tales from C++ Crypt:

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

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

// ... 

Nếu tôi chạy đó, hai chuyên ngành có nó mặc định đối số chính xác, nhưng phiên bản float nhận được cả hai đối số mặc định là zero (0,000000) mặc dù tốt hơn, nó vẫn không phải là z = 0w = 1.

Sửa 4:

Made một Connect issue.

+0

Tôi không thể thấy bất cứ điều gì sai trái với nó ... Bạn có thể thử nó dưới một trình biên dịch khác nhau? –

+1

Trình biên dịch nào bạn đang sử dụng và nền tảng nào là bạn? – Alerty

+1

Ah xin lỗi, quên thông tin đó, Microsoft VC++ 10 (16.00.30319.01). @ j_random_hacker: Hmm, tôi cũng có thể tải xuống MinGW và thử. – Skurmedel

Trả lời

5

Sau đây thất bại cho tôi trong 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 

Vì vậy, tôi cho rằng nó không hoạt động như mong đợi !!!

Nếu bạn loại bỏ việc khai báo trước và đặt đối số mặc định trong hàm mẫu thì nó hoạt động như mong đợi.

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

Điều này hoạt động như mong đợi. Điều này có liên quan đến việc mẫu trước khi khai báo không thực sự là một hàm được khai báo trước và do đó nó không thực sự có các tham số mặc định và như vậy bạn đang nhận được các giá trị ngẫu nhiên trong danh sách tham số.

OK. Không phải từ đọc sách của tôi của tiêu chuẩn này nên làm việc như mong đợi:

Sử dụng n2521
Mục 14.7.1 instantiation Implicit
Đoạn 9

An thực hiện sẽ không ngầm nhanh chóng một hàm mẫu, một mẫu thành viên , một thành viên không phải là thành viên ảo, một lớp thành viên hoặc một thành viên dữ liệu tĩnh của một mẫu lớp không yêu cầu sự khởi tạo. Nó là không xác định có hay không một thực hiện ngầm instantiates một chức năng thành viên ảo của một mẫu lớp nếu các chức năng thành viên ảo sẽ không nếu không được instantiated. Việc sử dụng một chuyên môn mẫu trong một đối số mặc định sẽ không làm cho mẫu được khởi tạo ngầm, ngoại trừ một mẫu lớp có thể được khởi tạo khi cần loại hoàn chỉnh của nó để xác định tính chính xác của đối số mặc định. Việc sử dụng đối số mặc định trong một cuộc gọi hàm gây ra các chuyên môn trong đối số mặc định được khởi tạo ngầm.

Phần in đậm của đoạn văn có vẻ (với tôi) để chỉ ra rằng mỗi chuyên môn được tạo do đối số mặc định sẽ được đưa vào đơn vị dịch khi được sử dụng.

Đoạn 11:

Nếu một hàm mẫu f được gọi theo một cách mà đòi hỏi phải có một biểu thức đối số mặc định sẽ được sử dụng, tên phụ thuộc được nhìn lên, ngữ nghĩa các ràng buộc được kiểm tra, và instantiation của bất kỳ mẫu nào được sử dụng trong biểu thức đối số mặc định được thực hiện như biểu thức đối số mặc định là một biểu thức được sử dụng trong chuyên môn khuôn mẫu hàm có cùng phạm vi, tham số mẫu giống nhau và quyền truy cập giống như của mẫu hàm f được sử dụng tại điểm đó . Phân tích này được gọi là instantiation instantiation. Đối số mặc định được khởi tạo sau đó được sử dụng làm đối số của f.

Cho biết rằng ngay cả khi đối số mặc định là thông số mẫu, chúng sẽ được tạo chính xác.

Tôi hy vọng tôi đã giải thích chính xác điều đó. :-)

+0

Bạn có thể thử gọi nó bằng hai chuyên ngành khác nhau (xem bản chỉnh sửa mới nhất của tôi), như sử dụng cả hai lần và nổi? Nó bắt đầu có mùi như VC++ đang làm điều gì đó vui nhộn ở đây. – Skurmedel

+0

Haha oh thân yêu, không bao giờ biết giá trị mặc định có thể là điều ác này. – Skurmedel

+0

Có ai đã thử điều này trên g ++/MinGW không?Nếu nó là C++ bất hợp pháp thì chắc chắn tiêu chuẩn C++ sẽ bắt buộc một thông báo lỗi biên dịch ...? –

1

Tôi không biết điều này có hiệu quả không, nhưng hãy thử sử dụng static_cast thay cho dàn diễn viên kiểu C cho các giá trị mặc định của bạn.

* Chỉnh sửa: Rõ ràng, vấn đề là trình biên dịch.

+0

Sản lượng cùng một kết quả đáng buồn. – Skurmedel

+3

Về mặt kỹ thuật, đó không phải là dàn diễn viên kiểu C mà là một cuộc gọi hàm tạo tham số. –

+0

@Drew Hall: Nó không phải là một lời gọi hàm tạo. – Alerty

2

Mã có được tối ưu hóa không? Có lẽ đó là lý do tại sao trình gỡ lỗi hiển thị cho bạn các giá trị sai.

Tôi đã thử mã đơn giản này (trong g ++ 4.3.3) và nó hoạt động như mong đợi.

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"; 
} 
+0

Nó cho thấy hành vi tương tự trong cả hai phát hành và gỡ lỗi (tối ưu hóa tắt), nhưng kiểm tra xem nếu nó tạo ra một sự khác biệt tôi phát hiện ra một điều kỳ lạ. Nó chỉ xảy ra nếu T là gấp đôi, không phải nếu nó nổi? – Skurmedel

Các vấn đề liên quan