2010-01-01 21 views
36

Trong khi thực hiện lập trình mẫu variadic trong C++ 11 trên GCC, thỉnh thoảng tôi nhận được lỗi có nội dung "Rất tiếc, chưa triển khai: không thể mở rộng 'Số nhận dạng ...' thành danh sách tăng độ dài cố định". Nếu tôi xóa "..." trong mã thì tôi gặp lỗi khác: "lỗi: các gói tham số không được mở rộng bằng '...'".Lỗi GCC với mẫu variadic: "Rất tiếc, chưa triển khai: không thể mở rộng 'Số nhận dạng ...' thành danh sách đối số có độ dài cố định"

Vì vậy, nếu tôi có "..." trong, GCC gọi là một lỗi, và nếu tôi lấy "..." ra, GCC gọi đó là một lỗi quá. Cách duy nhất tôi có thể giải quyết vấn đề này là viết lại hoàn toàn metaprogram mẫu từ đầu bằng cách sử dụng một cách tiếp cận khác, và (với may mắn) cuối cùng tôi đã tìm ra mã không gây ra lỗi. Nhưng tôi thực sự muốn biết những gì tôi đã làm sai. Mặc dù Googling cho nó và mặc dù thử nghiệm nhiều, tôi không thể pin xuống những gì nó là tôi đang làm khác nhau giữa mã mẫu variadic mà sản xuất lỗi này, và mã mà không có lỗi.

Từ ngữ của thông báo lỗi dường như ngụ ý rằng mã phải hoạt động theo tiêu chuẩn C++ 11, nhưng GCC chưa hỗ trợ nó. Hoặc có lẽ nó là một lỗi trình biên dịch?

Đây là một số mã tạo lỗi. Lưu ý: Tôi không cần bạn phải viết một thực hiện đúng đối với tôi, nhưng thay vì chỉ để chỉ ra những gì là về mã của tôi đó gây ra lỗi cụ thể này

// Used as a container for a set of types. 
template <typename... Types> struct TypePack 
{ 
    // Given a TypePack<T1, T2, T3> and T=T4, returns TypePack<T1, T2, T3, T4> 
    template <typename T> 
    struct Add 
    { 
     typedef TypePack<Types..., T> type; 
    }; 
}; 

// Takes the set (First, Others...) and, while N > 0, adds (First) to TPack. 
// TPack is a TypePack containing between 0 and N-1 types. 
template <int N, typename TPack, typename First, typename... Others> 
struct TypePackFirstN 
{ 
    // sorry, unimplemented: cannot expand ‘Others ...’ into a fixed-length argument list 
    typedef typename TypePackFirstN<N-1, typename TPack::template Add<First>::type, Others...>::type type; 
}; 

// The stop condition for TypePackFirstN: when N is 0, return the TypePack that has been built up. 
template <typename TPack, typename... Others> 
struct TypePackFirstN<0, TPack, Others...> //sorry, unimplemented: cannot expand ‘Others ...’ into a fixed-length argument list 
{ 
    typedef TPack type; 
}; 

EDIT: tôi đã nhận thấy trong khi đó một mẫu instantiation phần trông giống như không phải chịu các lỗi:

template <typename... T> 
struct SomeStruct<1, 2, 3, T...> {}; 

Viết lại nó như thế này không tạo ra một lỗi:

template <typename... T> 
struct SomeStruct<1, 2, 3, TypePack<T...>> {}; 

Có vẻ như bạn có thể khai báo các tham số cho các chuyên môn từng phần là variadic; tức là dòng này là OK:

template <typename... T> 

Nhưng bạn có thể không thực sự sử dụng những gói thông số trong chuyên môn, tức là phần này không phải là OK:

SomeStruct<1, 2, 3, T...> 

Thực tế là bạn có thể làm cho nó hoạt động nếu bạn quấn gói trong một số loại khác, tức là như thế này:

SomeStruct<1, 2, 3, TypePack<T...>> 

với tôi ngụ ý rằng khai của tham số variadic đến một chuyên môn mẫu từng phần đã thành công và bạn không thể sử dụng nó trực tiếp. bất cứ ai có thể xác nhận điều này?

+0

Nó có thể hoạt động ngay bây giờ với 'clang 3.2' – alfC

Trả lời

48

Có một mẹo để làm việc này với gcc. Tính năng này chưa được triển khai đầy đủ, nhưng bạn có thể cấu trúc mã để tránh các phần chưa được thực hiện. Mở rộng mẫu variadic theo cách thủ công thành danh sách tham số sẽ không hoạt động. Nhưng chuyên môn về mẫu có thể làm điều đó cho bạn.

template< char head, char ... rest > 
struct head_broken 
{ 
    static const char value = head; 
}; 

template< char ... all > 
struct head_works; // make the compiler hapy 

template< char head, char ... rest > 
struct head_works<head,rest...> // specialization 
{ 
    static const char value = head; 
}; 

template<char ... all > 
struct do_head 
{ 
    static const char head = head_works<all...>::value; 
    //Sorry, unimplemented: cannot expand 'all...' into a fixed-length arugment list 
    //static const char head = head_broken<all...>::value; 
}; 

int main 
{ 
    std::cout << head_works<'a','b','c','d'>::value << std::endl; 
    std::cout << head_broken<'a','b','c','d'>::value << std::endl; 
    std::cout << do_head<'a','b','c','d'>::head << std::endl; 
} 

Tôi thử nghiệm này với gcc 4.4.1

+0

Đây thực sự là câu trả lời hay nhất. Chuyên môn là cách để đi với gcc 4.4. –

+0

Tôi đã thay đổi điều này thành câu trả lời được chấp nhận bởi vì tôi thực sự chỉ sử dụng công việc xung quanh từ câu trả lời này và nó đã giải quyết được vấn đề của tôi. Lưu ý rằng nếu bạn có rất nhiều mã mẫu, sử dụng công việc này xung quanh dường như tiêu thụ nhiều thời gian/kích thước biên dịch hơn không. Tuy nhiên, sau khi phát hiện ra điều này đã tăng kích thước/thời gian xây dựng của tôi, tôi thấy rằng tôi có thể cuộn nó thành một mẫu bên ngoài mà tôi đã có, và đã phân bổ chi phí bằng cách sử dụng chuyên môn cho công việc xung quanh. – Dennis

+0

Cảm ơn, điều này làm việc như một say mê. – terminus

2

Bạn đang sử dụng phiên bản GCC nào? Theo điều này GCC status page, GCC 4.4 nên hỗ trợ nó.

Thử nghiệm với GCC 4.4.2, tôi nhận được lỗi tương tự.

The wording of the error message seems to imply that the code should work according the C++0x standard, but that GCC doesn't support it yet. Or perhaps it is a compiler bug?

Điều này đúng, GCC hiểu mã nhưng không thể nhổ ra GIMPLE cho nó.

Đối với nguyên nhân gây ra lỗi, đó là phần mở rộng của danh sách biến mẫu vào danh sách biến của mẫu khác.

+0

Tôi đang sử dụng GCC 4.4.1. – Dennis

+0

Bạn có thể giải thích đầy đủ hơn về việc mở rộng danh sách biến mẫu vào danh sách mẫu khác không? Tôi đã bắt đầu nghĩ rằng nó đã làm với không thể sử dụng các gói mẫu variadic trực tiếp như là đối số cho một chuyên môn mẫu. – Dennis

+2

Nếu việc mở rộng gói tạo ra một danh sách đối số mẫu trong đó một số đối số nhấn tham số cố định và một số tham số nhấn vào một gói tham số mẫu, thì lỗi này dường như xảy ra. Tôi viết lại mã của bạn để điều này, và nó hoạt động: http://codepad.org/IBT60IbJ (tôi đã phải giới thiệu một chuyên môn hóa một phần, để làm cho trường hợp '<0, A, B>' chọn một chuyên môn uniq). Trong trường hợp này, mỗi gói mở rộng luôn chỉ chạm vào một gói tham số tương ứng. –

3

Theo như tôi hiểu được những lỗi được báo cáo bởi vì trình biên dịch thấy việc kê khai của mẫu lớp như một lớp học với ít nhất 3 đối số tiếp theo là tùy chọn những người. Vì bạn cố gắng tham chiếu đến nó với 2 đối số theo sau bởi danh sách mở rộng, nó bị nhầm lẫn và gây ra lỗi này. Để biên dịch chính xác, bạn chỉ cần khai báo mẫu đầu tiên như sau:

template <int N, typename TPack, typename... Others> 
struct TypePackFirstN; 

Và sau đó định nghĩa bước đệ quy phải được đặt lại làm chuyên môn mẫu. (điều này thực hiện thủ thuật với gcc 4.5.0 20100404).

// Takes the set (First, Others...) and, while N > 0, adds (First) to TPack. 
// TPack is a TypePack containing between 0 and N-1 types. 
template <int N, typename TPack, typename First, typename... Others> 
struct TypePackFirstN<N, TPack, First, Others...> 
{ 
    // Error "sorry, unimplemented: cannot expand ‘Others ...’ into a fixed-length argument list" should be now gone 
    typedef typename TypePackFirstN<N-1, typename TPack::template Add<First>::type, Others...>::type type; 
}; 

// The stop condition for TypePackFirstN: when N is 0, return the TypePack that has been built up. 
template <typename TPack, typename... Others> 
struct TypePackFirstN<0, TPack, Others...>   // Error "sorry, unimplemented: cannot expand ‘Others ...’ into a fixed-length argument list" should be now gone 
{ 
    typedef TPack type; 
}; 
1

Câu trả lời của deft_code là chính xác. Tôi đang đăng bài này chỉ trong trường hợp điều này hữu ích khi thấy so sánh song song giữa mã bị hỏng và mã cố định.

Tôi sẽ đưa các mẫu mã từ những câu dưới đây một người nào đó đăng tải đã được nhân đôi với trang này và hiện đang đóng: Is ther a good workaround for GCC's "sorry, unimplemented: cannot expand ‘NEXT ...’ into a fixed-length argument list" error?

#include <iostream> 

template <int FIRST, int... NEXT> 
struct Test { 
    static const int VALUE = FIRST + Test<NEXT...>::VALUE; 
}; 

template <int FIRST> 
struct Test<FIRST> { 
    static const int VALUE = FIRST; 
}; 

int main() { 
    std::cout << Test<1, 2, 3>::VALUE << std::endl; // print "6" 
    return 0; 
} 

này, khi biên soạn, cung cấp cho:

g++ -std=c++11 -o test test.cc 
test.cc:5:50: sorry, unimplemented: cannot expand âNEXT ...â into a fixed-length argument list 

Nhưng công trình này (tôi đã thêm nhận xét nơi mã đã được thay đổi):

#include <iostream> 

template <int ... ALL> // Adeed 
struct Test;   // Added 

template <int FIRST, int... NEXT> 
struct Test<FIRST, NEXT...> { // Note: specialized with <FIRST, NEXT...> 
    static const int VALUE = FIRST + Test<NEXT...>::VALUE; 
}; 

template <int FIRST> 
struct Test<FIRST> { 
    static const int VALUE = FIRST; 
}; 

int main() { 
    std::cout << Test<1, 2, 3>::VALUE << std::endl; // print "6" 
    return 0; 
} 

Lưu ý những gì đã được thực hiện trong thay đổi ba dòng này được đánh dấu bởi các ý kiến: những gì ban đầu là mẫu đầu tiên đã được thực hiện một mẫu chuyên biệt của mẫu variadic mới được bổ sung.

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