2012-01-28 27 views
15

này được sử dụng để làm việc một vài tuần trước:C++ 11 constexpr chức năng của thông qua vào mẫu luận

template <typename T, T t> 
T   tfunc() 
{ 
    return t + 10; 
} 

template <typename T> 
constexpr T  func(T t) 
{ 
    return tfunc<T, t>(); 
} 

int main() 
{ 
    std::cout << func(10) << std::endl; 
    return 0; 
} 

Nhưng bây giờ g++ -std=c++0x nói:

main.cpp: In function ‘constexpr T func(T) [with T = int]’: 
main.cpp:29:25: instantiated from here 
main.cpp:24:24: error: no matching function for call to ‘tfunc()’ 
main.cpp:24:24: note: candidate is: 
main.cpp:16:14: note: template<class T, T t> T tfunc() 
main.cpp:25:1: warning: control reaches end of non-void function [-Wreturn-type] 

clang++ -std=c++11 nói rằng các thông số mẫu của tfunc<T, t>() được bỏ qua vì không hợp lệ.

Đó có phải là lỗi hoặc bản sửa lỗi không?

PS:

g++ --version =>g++ (GCC) 4.6.2 20120120 (prerelease)

clang++ --version =>clang version 3.0 (tags/RELEASE_30/final) (3.0.1)

+0

FWIW, Clang 3.1 HEAD cũng phun ra các lỗi tương tự. – Xeo

Trả lời

7

Tham số t không phải là một biểu thức hằng. Do đó lỗi. Cũng nên lưu ý rằng nó không thể là một biểu thức liên tục.

Bạn có thể chuyển biểu thức hằng số làm đối số, nhưng bên trong hàm, đối tượng (tham số) giữ giá trị, không phải là biểu thức liên tục.

Kể từ t không phải là một biểu thức hằng số, nó không thể được sử dụng như mẫu đối số:

return tfunc<T, t>(); //the second argument must be a constant expression 

Có lẽ, bạn muốn một cái gì đó như thế này:

template <typename T, T t> 
T tfunc() 
{ 
    return t + 10; 
} 

template <typename T, T t> //<---- t became template argument! 
constexpr T func() 
{ 
    return tfunc<T, t>(); 
} 

#define FUNC(a) func<decltype(a),a>() 

int main() 
{ 
    std::cout << FUNC(10) << std::endl; 
} 

Bây giờ nó nên làm việc: online demo

+0

Tôi vẫn còn bối rối bởi câu: * một đối số cho một chức năng không thể là một biểu thức const * –

+0

@ Mr.Anubis: Đó là lý do tại sao tôi giải thích rằng một phần trong đoạn tiếp theo. Bạn đã đọc nó chưa? – Nawaz

+0

bạn có nghĩa là '..func (T t)' bên trong 'func',' t' trong không constexp, phải không? –

1

Có vẻ như nó sẽ đưa ra lỗi - không có cách nào biết rằng bạn đã vượt qua trong một giá trị không đổi như t để func.

Nói chung, bạn không thể sử dụng giá trị thời gian chạy làm đối số mẫu. Mẫu vốn đã là một cấu trúc biên dịch.

+2

Và các hàm 'constexpr' có thể được đánh giá tại thời gian biên dịch. – Xeo

+3

@Xeo Tôi không thấy mức độ phù hợp của nhận xét của bạn đối với câu trả lời này. Nghe có vẻ như, tôi nói "Bàn tay của tôi không thể được sử dụng để đi bộ xung quanh." và bạn nói "Và cánh tay của bạn có thể được sử dụng để di chuyển bàn tay của bạn." –

+0

rofl tương tự: D –

2

Tôi có cảm giác rằng constexpr cũng phải hợp lệ trong ngữ cảnh 'thời gian chạy', không chỉ ở thời gian biên dịch. Đánh dấu một hàm là constexpr khuyến khích trình biên dịch thử đánh giá nó tại thời gian biên dịch, nhưng hàm vẫn phải có một thời gian chạy hợp lệ.

Trong thực tế, điều này có nghĩa rằng trình biên dịch không biết làm thế nào để thực hiện chức năng này trong thời gian chạy:

template <typename T> 
constexpr T  func(T t) 
{ 
    return tfunc<T, t>(); 
} 

Một cách giải quyết là thay đổi các nhà xây dựng như vậy mà nó có tham số t của nó như là một tham số bình thường, không phải là một mẫu tham số, và đánh dấu các nhà xây dựng như constexpr:

template <typename T> 
constexpr T  tfunc(T t) 
{ 
    return t + 10; 
} 
template <typename T> 
constexpr T  func(T t) 
{ 
    return tfunc<T>(t); 
} 

có ba mức độ 'liên tục thể hiện-Ness':

  1. mẫu int tham số, hoặc (phi VLA) kích thước mảng // Cái gì đó phải là một hằng số thể hiện
  2. constexpr // Cái gì đó thể là một hằng số thể hiện
  3. không liên tục -expression

Bạn thực sự không thể chuyển đổi các mục có ít danh sách đó thành danh sách cao trong danh sách đó, nhưng rõ ràng là tuyến đường khác có thể.

Ví dụ, một cuộc gọi đến chức năng này

constexpr int foo(int x) { return x+1; } 

không nhất thiết phải là một hằng số thể hiện.

// g++-4.6 used in these few lines. ideone doesn't like this code. I don't know why 
int array[foo(3)]; // this is OK 
int c = getchar(); 
int array[foo(c)]; // this will not compile (without VLAs) 

Vì vậy, giá trị trả về từ một hàm constexpr là một biểu thức hằng chỉ khi tất cả các thông số, và thực hiện các chức năng, có thể được hoàn thành vào thực hiện tại thời gian biên dịch.

+0

'constexpr int i = something_that_MUST_be_a_constexpr;' – Xeo

+0

@Xeo, nó phải * ít nhất * là 'constexpr'. Trong khi đó, 'mẫu struct X; X x; 'yêu cầu' i' thậm chí còn lớn hơn 'constexpr'. –

+0

Có lẽ tôi không nên sử dụng 'constexpr' thứ hai dưới dạng viết tắt cho' biểu thức liên tục' ... :) Tôi chỉ muốn nói rằng đối với 'constexpr', đôi khi một cái gì đó * phải * là một biểu thức liên tục - khi một biến được khai báo như vậy. (Như một câu trả lời cho "' constexpr' // Cái gì đó * có thể * là một biểu thức liên tục ".) – Xeo

2

Thu hồi câu hỏi: Bạn có hai hàm lấy tham số loại T. Một tham số của nó là tham số mẫu và tham số kia dưới dạng tham số 'bình thường'. Tôi sẽ gọi hai chức năng funcTfuncN thay vì tfuncfunc. Bạn muốn có thể gọi funcT từ funcN. Đánh dấu phần sau là constexpr không giúp ích gì.

Bất kỳ chức năng nào được đánh dấu là constexpr phải được đồng bộ hóa như thể constexpr không có ở đó. constexpr chức năng là một chút tâm thần phân liệt. Họ chỉ tốt nghiệp với các biểu thức liên tục đầy đủ trong một số trường hợp nhất định.

Nó sẽ không thể thực hiện funcN để chạy trong thời gian chạy một cách đơn giản, vì nó sẽ cần để có thể làm việc cho tất cả giá trị có thể của t. Điều này sẽ yêu cầu trình biên dịch để khởi tạo nhiều trường hợp của tfunc, một cho mỗi giá trị của t. Nhưng bạn có thể giải quyết vấn đề này nếu bạn sẵn sàng sống với một nhóm nhỏ T.Có một giới hạn mẫu-đệ quy 1024 trong g ++, vì vậy bạn có thể dễ dàng xử lý 1024 giá trị của T với mã này:

#include<iostream> 
#include<functional> 
#include<array> 
using namespace std; 

template <typename T, T t> 
constexpr T funcT() { 
     return t + 10; 
} 

template<typename T, T u> 
constexpr T worker (T t) { 
     return t==0 ? funcT<T,u>() : worker<T, u+1>(t-1); 

} 
template<> 
constexpr int worker<int,1000> (int) { 
      return -1; 
} 


template <typename T> 
constexpr T  funcN(T t) 
{ 
     return t<1000 ? worker<T,0>(t) : -1; 
} 

int main() 
{ 
    std::cout << funcN(10) << std::endl; 
    array<int, funcN(10)> a; // to verify that funcN(10) returns a constant-expression 
    return 0; 
} 

Nó sử dụng một hàm worker mà đệ quy sẽ chuyển đổi 'bình thường' tham số t thành một mẫu tham số u, sau đó nó sử dụng để khởi tạo và thực hiện tfunc<T,u>.

Dòng quan trọng là return funcT<T,u>() : worker<T, u+1>(t-1);

Điều này có giới hạn. Nếu bạn muốn sử dụng long hoặc các loại tích phân khác, bạn sẽ phải thêm một chuyên môn khác. Rõ ràng, mã này chỉ hoạt động cho t từ 0 đến 1000 - giới hạn trên chính xác có thể phụ thuộc vào trình biên dịch. Một lựa chọn khác có thể được sử dụng tìm kiếm nhị phân của các loại, với một chức năng lao động khác nhau cho mỗi sức mạnh của 2:

template<typename T, T u> 
constexpr T worker4096 (T t) { 
     return t>=4096 ? worker2048<T, u+4096>(t-4096) : worker2048<T, u>(t); 

} 

Tôi nghĩ rằng điều này sẽ làm việc xung quanh hạn template-đệ quy, nhưng nó vẫn sẽ đòi hỏi rất số lượng lớn các cảnh báo và sẽ làm cho biên dịch rất chậm, nếu nó hoạt động.

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