Các mẫu C ++ chỉ chấp nhận một số loại nhất định


158

Trong Java, bạn có thể định nghĩa lớp chung chỉ chấp nhận các kiểu mở rộng lớp bạn chọn, ví dụ:

public class ObservableList<T extends List> {
  ...
}

Điều này được thực hiện bằng cách sử dụng từ khóa "mở rộng".

Có một số đơn giản tương đương với từ khóa này trong C ++?


Câu hỏi khá cũ đã ... Tôi cảm thấy điều còn thiếu ở đây (cũng từ các câu trả lời) là các khái quát về Java không thực sự tương đương với các mẫu trong C ++. Có nhiều điểm tương đồng, nhưng tôi nên cẩn thận với việc dịch trực tiếp một giải pháp java sang C ++ để nhận ra rằng chúng có thể được tạo ra cho các loại vấn đề khác nhau;)
idclev 463035818

Câu trả lời:


104

Tôi khuyên bạn nên sử dụng tính năng xác nhận tĩnh của Boost trong buổi hòa nhạc với is_base_ofthư viện Traits loại Boost:

template<typename T>
class ObservableList {
    BOOST_STATIC_ASSERT((is_base_of<List, T>::value)); //Yes, the double parentheses are needed, otherwise the comma will be seen as macro argument separator
    ...
};

Trong một số trường hợp khác, đơn giản hơn, bạn chỉ có thể chuyển tiếp khai báo một mẫu toàn cục, nhưng chỉ xác định (chuyên môn hóa một phần hoặc rõ ràng) cho các loại hợp lệ:

template<typename T> class my_template;     // Declare, but don't define

// int is a valid type
template<> class my_template<int> {
    ...
};

// All pointer types are valid
template<typename T> class my_template<T*> {
    ...
};

// All other types are invalid, and will cause linker error messages.

[EDIT nhỏ 6/12/2013: Sử dụng mẫu được khai báo nhưng không được xác định sẽ dẫn đến trình liên kết , không phải trình biên dịch, thông báo lỗi.]


Khẳng định tĩnh là tốt đẹp. :)
macbirdie

5
@ John: Tôi sợ rằng chuyên môn hóa sẽ chỉ phù hợp myBaseTypechính xác. Trước khi loại bỏ Boost, bạn nên biết rằng hầu hết trong số đó là mã mẫu chỉ có tiêu đề - vì vậy không có chi phí bộ nhớ hoặc thời gian trong thời gian chạy cho những thứ bạn không sử dụng. Ngoài ra, những điều cụ thể bạn đang sử dụng ở đây ( BOOST_STATIC_ASSERT()is_base_of<>) chỉ có thể được triển khai bằng cách sử dụng các khai báo (nghĩa là không có định nghĩa thực tế về hàm hoặc biến) để chúng sẽ không mất bất kỳ không gian hoặc thời gian nào.
j_random_hacker

50
C ++ 11 đã đến. Bây giờ chúng ta có thể sử dụng static_assert(std::is_base_of<List, T>::value, "T must extend list").
Siyuan Ren

2
BTW, lý do dấu ngoặc kép là cần thiết là BOOST_STATIC_ASSERT là một macro và dấu ngoặc đơn phụ ngăn bộ tiền xử lý diễn giải dấu phẩy trong các đối số hàm is_base_of làm đối số macro thứ hai.
jfritz42

1
@Andreyua: Tôi không thực sự hiểu những gì còn thiếu. Bạn có thể thử khai báo một biến my_template<int> x;hoặc my_template<float**> y;xác minh rằng trình biên dịch cho phép các biến này, sau đó khai báo một biến my_template<char> z;và xác minh rằng nó không.
j_random_hacker

134

Điều này thường không có cơ sở trong C ++, như các câu trả lời khác ở đây đã lưu ý. Trong C ++, chúng tôi có xu hướng xác định các loại chung dựa trên các ràng buộc khác ngoài "kế thừa từ lớp này". Nếu bạn thực sự muốn làm điều đó, thì khá dễ thực hiện trong C ++ 11 và <type_traits>:

#include <type_traits>

template<typename T>
class observable_list {
    static_assert(std::is_base_of<list, T>::value, "T must inherit from list");
    // code here..
};

Điều này phá vỡ rất nhiều khái niệm mà mọi người mong đợi trong C ++. Tốt hơn là sử dụng các thủ thuật như xác định đặc điểm của riêng bạn. Ví dụ, có thể observable_listmuốn chấp nhận bất kỳ loại container nào có typedefs const_iteratorvà hàm beginendthành viên trả về const_iterator. Nếu bạn hạn chế điều này đối với các lớp kế thừa từ listđó thì người dùng có kiểu riêng không thừa kế listnhưng cung cấp các hàm thành viên này và typedefs sẽ không thể sử dụng observable_list.

Có hai giải pháp cho vấn đề này, một trong số đó là không ép buộc bất cứ điều gì và dựa vào việc gõ vịt. Một vấn đề lớn đối với giải pháp này là nó liên quan đến một số lượng lớn lỗi có thể khiến người dùng khó có thể mò mẫm. Một giải pháp khác là xác định các đặc điểm để hạn chế loại được cung cấp để đáp ứng các yêu cầu giao diện. Con lừa lớn cho giải pháp này là liên quan đến việc viết thêm có thể được xem là gây phiền nhiễu. Tuy nhiên, mặt tích cực là bạn sẽ có thể viết thông báo lỗi của riêng bạn a la static_assert.

Để hoàn thiện, giải pháp cho ví dụ trên được đưa ra:

#include <type_traits>

template<typename...>
struct void_ {
    using type = void;
};

template<typename... Args>
using Void = typename void_<Args...>::type;

template<typename T, typename = void>
struct has_const_iterator : std::false_type {};

template<typename T>
struct has_const_iterator<T, Void<typename T::const_iterator>> : std::true_type {};

struct has_begin_end_impl {
    template<typename T, typename Begin = decltype(std::declval<const T&>().begin()),
                         typename End   = decltype(std::declval<const T&>().end())>
    static std::true_type test(int);
    template<typename...>
    static std::false_type test(...);
};

template<typename T>
struct has_begin_end : decltype(has_begin_end_impl::test<T>(0)) {};

template<typename T>
class observable_list {
    static_assert(has_const_iterator<T>::value, "Must have a const_iterator typedef");
    static_assert(has_begin_end<T>::value, "Must have begin and end member functions");
    // code here...
};

Có rất nhiều khái niệm được hiển thị trong ví dụ trên thể hiện các tính năng của C ++ 11. Một số thuật ngữ tìm kiếm cho sự tò mò là các mẫu từ điển, SFINAE, SFINAE biểu thức và các đặc điểm loại.


2
Tôi chưa bao giờ nhận ra các mẫu C ++ sử dụng kiểu gõ vịt cho đến ngày hôm nay. Kiểu kỳ quái!
Andy

2
Với các ràng buộc chính sách mở rộng, C ++ được giới thiệu cho C , không chắc tại sao lại template<class T:list>là một khái niệm vi phạm như vậy. Cảm ơn vì tiền hỗ trợ.
bvj

60

Giải pháp đơn giản mà chưa ai nhắc đến là bỏ qua vấn đề này. Nếu tôi cố gắng sử dụng một intkiểu mẫu trong một mẫu hàm mong đợi một lớp chứa như vectơ hoặc danh sách, thì tôi sẽ gặp lỗi biên dịch. Thô lỗ và đơn giản, nhưng nó giải quyết vấn đề. Trình biên dịch sẽ cố gắng sử dụng loại bạn chỉ định và nếu thất bại, nó sẽ tạo ra lỗi biên dịch.

Vấn đề duy nhất là thông báo lỗi bạn nhận được sẽ rất khó đọc. Tuy nhiên, đây là một cách rất phổ biến để làm điều này. Thư viện tiêu chuẩn có đầy đủ các mẫu chức năng hoặc lớp dự kiến ​​hành vi nhất định từ loại mẫu và không làm gì để kiểm tra xem các loại được sử dụng có hợp lệ không.

Nếu bạn muốn thông báo lỗi đẹp hơn (hoặc nếu bạn muốn bắt các trường hợp không tạo ra lỗi trình biên dịch, nhưng vẫn không có ý nghĩa), bạn có thể, tùy thuộc vào mức độ phức tạp mà bạn muốn thực hiện, hãy sử dụng khẳng định tĩnh của Boost hoặc thư viện Boost concept_check.

Với trình biên dịch cập nhật, bạn có một build_in static_assert, có thể được sử dụng thay thế.


7
Có, tôi luôn nghĩ rằng các mẫu là thứ gần gũi nhất với kiểu gõ vịt trong C ++. Nếu nó có tất cả các yếu tố cần thiết cho một mẫu, nó có thể được sử dụng trong một mẫu.

@ John: Tôi xin lỗi, tôi không thể làm được điều đó. Loại này là gì T, và mã này được gọi từ đâu? Không có một số bối cảnh, tôi không có cơ hội hiểu đoạn mã đó. Nhưng những gì tôi nói là đúng. Nếu bạn cố gắng gọi toString()một loại không có toStringchức năng thành viên, thì bạn sẽ gặp lỗi biên dịch.
jalf

@ John: thời gian tới, có lẽ bạn nên có một chút ít cò-hạnh phúc downvoting người khi vấn đề là trong mã của bạn
jalf

@jalf, ok. +1. Đây là một câu trả lời tuyệt vời chỉ cố gắng làm cho nó tốt nhất. Xin lỗi vì đã đọc sai. Tôi nghĩ rằng chúng ta đã nói về việc sử dụng kiểu làm tham số cho các lớp không dành cho các mẫu hàm, mà tôi cho là các thành viên trước đây nhưng cần gọi trình biên dịch để gắn cờ.
Giăng

13

Chúng tôi có thể sử dụng std::is_base_ofstd::enable_if:
( static_assertcó thể được gỡ bỏ, các lớp trên có thể được triển khai tùy chỉnh hoặc được sử dụng từ boost nếu chúng tôi không thể tham khảo type_traits)

#include <type_traits>
#include <list>

class Base {};
class Derived: public Base {};

#if 0   // wrapper
template <class T> class MyClass /* where T:Base */ {
private:
    static_assert(std::is_base_of<Base, T>::value, "T is not derived from Base");
    typename std::enable_if<std::is_base_of<Base, T>::value, T>::type inner;
};
#elif 0 // base class
template <class T> class MyClass: /* where T:Base */
    protected std::enable_if<std::is_base_of<Base, T>::value, T>::type {
private:
    static_assert(std::is_base_of<Base, T>::value, "T is not derived from Base");
};
#elif 1 // list-of
template <class T> class MyClass /* where T:list<Base> */ {
    static_assert(std::is_base_of<Base, typename T::value_type>::value , "T::value_type is not derived from Base");
    typedef typename std::enable_if<std::is_base_of<Base, typename T::value_type>::value, T>::type base; 
    typedef typename std::enable_if<std::is_base_of<Base, typename T::value_type>::value, T>::type::value_type value_type;

};
#endif

int main() {
#if 0   // wrapper or base-class
    MyClass<Derived> derived;
    MyClass<Base> base;
//  error:
    MyClass<int> wrong;
#elif 1 // list-of
    MyClass<std::list<Derived>> derived;
    MyClass<std::list<Base>> base;
//  error:
    MyClass<std::list<int>> wrong;
#endif
//  all of the static_asserts if not commented out
//  or "error: no type named ‘type’ in ‘struct std::enable_if<false, ...>’ pointing to:
//  1. inner
//  2. MyClass
//  3. base + value_type
}

13

Theo như tôi biết thì điều này hiện không thể có trong C ++. Tuy nhiên, có kế hoạch thêm một tính năng gọi là "khái niệm" trong tiêu chuẩn C ++ 0x mới cung cấp chức năng mà bạn đang tìm kiếm. Bài viết Wikipedia này về khái niệm C ++ sẽ giải thích chi tiết hơn.

Tôi biết điều này không khắc phục được sự cố tức thời của bạn nhưng có một số trình biên dịch C ++ đã bắt đầu thêm các tính năng từ tiêu chuẩn mới, vì vậy có thể tìm thấy trình biên dịch đã triển khai tính năng khái niệm.


4
Các khái niệm đã bị loại bỏ khỏi tiêu chuẩn không may.
macbirdie

4
Các ràng buộc và khái niệm nên được áp dụng cho C ++ 20.
Petr Javorik

Có thể ngay cả khi không có khái niệm, sử dụng static_assertvà SFINAE, như các câu trả lời khác cho thấy. Vấn đề còn lại đối với ai đó đến từ Java hoặc C # hoặc Haskell (...) là trình biên dịch C ++ 20 không thực hiện kiểm tra định nghĩa đối với các khái niệm bắt buộc, mà Java và C # làm.
user7610

10

Tôi nghĩ rằng tất cả các câu trả lời trước đã mất tầm nhìn của rừng cho cây.

Java generic không giống như các mẫu ; họ sử dụng kiểu xóa , đây là một kỹ thuật động , thay vì đa hình thời gian , là kỹ thuật tĩnh . Rõ ràng là tại sao hai chiến thuật rất khác nhau này không tốt.

Thay vì cố gắng sử dụng cấu trúc thời gian biên dịch để mô phỏng thời gian chạy, hãy xem xét những gì extendsthực sự làm: theo Stack OverflowWikipedia , extends được sử dụng để biểu thị phân lớp.

C ++ cũng hỗ trợ phân lớp.

Bạn cũng hiển thị một lớp container, đang sử dụng kiểu xóa ở dạng chung và mở rộng để thực hiện kiểm tra kiểu. Trong C ++, bạn phải tự thực hiện loại máy xóa, rất đơn giản: tạo một con trỏ tới siêu lớp.

Chúng ta hãy bọc nó thành một typedef, để dễ sử dụng hơn là tạo cả một lớp, et voila:

typedef std::list<superclass*> subclasses_of_superclass_only_list;

Ví dụ:

class Shape { };
class Triangle : public Shape { };

typedef std::list<Shape*> only_shapes_list;
only_shapes_list shapes;

shapes.push_back(new Triangle()); // Works, triangle is kind of shape
shapes.push_back(new int(30)); // Error, int's are not shapes

Bây giờ, có vẻ như List là một giao diện, đại diện cho một bộ sưu tập. Một giao diện trong C ++ sẽ chỉ là một lớp trừu tượng, nghĩa là một lớp không thực hiện gì ngoài các phương thức ảo thuần túy. Sử dụng phương pháp này, bạn có thể dễ dàng triển khai ví dụ java của mình trong C ++, mà không cần bất kỳ khái niệm hoặc chuyên môn mẫu nào. Nó cũng sẽ hoạt động chậm như các tổng quát kiểu Java do tra cứu bảng ảo, nhưng điều này thường có thể là một mất mát chấp nhận được.


3
Tôi không phải là người thích câu trả lời sử dụng các cụm từ như "nó nên rõ ràng" hoặc "mọi người đều biết", và sau đó tiếp tục giải thích những gì rõ ràng hoặc được mọi người biết đến. Rõ ràng là liên quan đến bối cảnh, kinh nghiệm và bối cảnh của kinh nghiệm. Những tuyên bố như vậy vốn đã thô lỗ.
3Dave

2
@DavidLively Đó là khoảng hai năm quá muộn để chỉ trích câu trả lời này cho nghi thức, nhưng tôi cũng không đồng ý với bạn trong trường hợp cụ thể này; Tôi đã giải thích lý do tại sao hai kỹ thuật này không đi cùng nhau trước khi nói rõ ràng, không phải sau đó. Tôi đã cung cấp bối cảnh, và sau đó nói kết luận từ bối cảnh đó là rõ ràng. Điều đó không chính xác phù hợp với khuôn của bạn.
Alice

Tác giả của câu trả lời này cho biết một cái gì đó là rõ ràng sau khi thực hiện một số nâng nặng. Tôi không nghĩ rằng tác giả có ý định nói giải pháp là hiển nhiên.
Luke Gehorsam

10

Một tương đương chỉ chấp nhận các loại T xuất phát từ loại Danh sách trông giống như

template<typename T, 
         typename std::enable_if<std::is_base_of<List, T>::value>::type* = nullptr>
class ObservableList
{
    // ...
};

8

Tóm tắt điều hành: Đừng làm điều đó.

câu trả lời của j_random_hacker cho bạn biết làm thế nào để làm điều này. Tuy nhiên, tôi cũng muốn chỉ ra rằng bạn không nên làm điều này. Điểm chung của các mẫu là chúng có thể chấp nhận bất kỳ loại tương thích nào và các ràng buộc kiểu kiểu Java phá vỡ điều đó.

Các ràng buộc kiểu của Java là một lỗi không phải là một tính năng. Chúng ở đó bởi vì Java không xóa kiểu tổng quát, vì vậy Java không thể tìm ra cách gọi các phương thức chỉ dựa trên giá trị của các tham số kiểu.

C ++ mặt khác không có hạn chế như vậy. Các loại tham số mẫu có thể là bất kỳ loại nào tương thích với các hoạt động mà chúng được sử dụng. Không cần phải có một lớp cơ sở chung. Điều này tương tự như "Duck Typing" của Python, nhưng được thực hiện vào thời gian biên dịch.

Một ví dụ đơn giản cho thấy sức mạnh của các mẫu:

// Sum a vector of some type.
// Example:
// int total = sum({1,2,3,4,5});
template <typename T>
T sum(const vector<T>& vec) {
    T total = T();
    for (const T& x : vec) {
        total += x;
    }
    return total;
}

Hàm sum này có thể tổng hợp một vectơ thuộc bất kỳ loại nào hỗ trợ các thao tác chính xác. Nó hoạt động với cả hai nguyên hàm như int / long / float / double và các kiểu số do người dùng xác định làm quá tải toán tử + =. Heck, bạn thậm chí có thể sử dụng chức năng này để tham gia chuỗi, vì chúng hỗ trợ + =.

Không có quyền anh / unboxing của người nguyên thủy là cần thiết.

Lưu ý rằng nó cũng xây dựng các phiên bản mới của T bằng cách sử dụng T (). Điều này là tầm thường trong C ++ khi sử dụng các giao diện ngầm định, nhưng không thực sự có thể có trong Java với các ràng buộc kiểu.

Mặc dù các mẫu C ++ không có ràng buộc kiểu rõ ràng, chúng vẫn là loại an toàn và sẽ không biên dịch với mã không hỗ trợ các hoạt động chính xác.


2
Nếu bạn đang đề xuất các mẫu không bao giờ chuyên, bạn cũng có thể giải thích tại sao nó có trong ngôn ngữ?

1
Tôi hiểu ý của bạn, nhưng nếu đối số mẫu của bạn phải xuất phát từ một loại cụ thể, thì tốt hơn là nên có một thông báo dễ hiểu từ static_assert so với nôn trình biên dịch thông thường.
jhoffman0x

1
Đúng, C ++ biểu cảm hơn ở đây, nhưng trong khi đó nói chung là một điều tốt (vì chúng ta có thể thể hiện nhiều hơn với ít hơn), đôi khi chúng ta muốn cố tình hạn chế sức mạnh mà chúng ta cung cấp cho mình, để có được sự chắc chắn rằng chúng ta hoàn toàn hiểu một hệ thống.
j_random_hacker

Loại @Curg chuyên dụng rất hữu ích khi bạn muốn có thể tận dụng một số thứ chỉ có thể được thực hiện cho một số loại nhất định. ví dụ, một boolean là ~ bình thường ~ mỗi byte một, mặc dù một byte có thể ~ bình thường ~ giữ 8 bit / booleans; một lớp bộ sưu tập mẫu có thể (và trong trường hợp std :: map hiện) chuyên về boolean để nó có thể đóng gói dữ liệu chặt chẽ hơn để bảo tồn bộ nhớ.
thecoshman

Ngoài ra, để làm rõ, câu trả lời này không phải là "không bao giờ chuyên môn hóa mẫu" mà nói rằng đừng sử dụng tính năng đó để cố gắng hạn chế loại nào có thể được sử dụng với một mẫu.
thecoshman

6

Điều đó không thể có trong C ++ đơn giản, nhưng bạn có thể xác minh các tham số mẫu tại thời gian biên dịch thông qua Kiểm tra khái niệm, ví dụ: sử dụng BCCL của Boost .

Kể từ C ++ 20, các khái niệm đang trở thành một tính năng chính thức của ngôn ngữ.


2
Vâng, đó có thể, nhưng khái niệm kiểm tra vẫn còn là một ý tưởng tốt. :)
j_random_hacker

Tôi thực sự có nghĩa là không thể có trong C ++ "đơn giản". ;)
macbirdie

5
class Base
{
    struct FooSecurity{};
};

template<class Type>
class Foo
{
    typename Type::FooSecurity If_You_Are_Reading_This_You_Tried_To_Create_An_Instance_Of_Foo_For_An_Invalid_Type;
};

Hãy chắc chắn rằng các lớp dẫn xuất kế thừa cấu trúc FooSecurity và trình biên dịch sẽ bị đảo lộn ở tất cả các vị trí thích hợp.


@Zehelvion Type::FooSecurityđược sử dụng trong lớp mẫu. Nếu lớp, được truyền trong đối số mẫu, không FooSecurity, cố gắng sử dụng nó gây ra lỗi. Chắc chắn rằng nếu lớp được truyền trong đối số mẫu không có FooSecurance thì nó không xuất phát từ đó Base.
GingerPlusPlus

2

Sử dụng khái niệm C ++ 20

https://en.cppreference.com/w/cpp/lingu/constraint cppreference đang đưa ra trường hợp sử dụng thừa kế như một ví dụ khái niệm rõ ràng:

template <class T, class U>
concept Derived = std::is_base_of<U, T>::value;
 
template<Derived<Base> T>
void f(T);  // T is constrained by Derived<T, Base>

Đối với nhiều cơ sở, tôi đoán cú pháp sẽ là:

template <class T, class U, class V>
concept Derived = std::is_base_of<U, T>::value || std::is_base_of<V, T>::value;
 
template<Derived<Base1, Base2> T>
void f(T);

GCC 10 dường như đã triển khai nó: https://gcc.gnu.org/gcc-10/changes.html và bạn có thể lấy nó dưới dạng PPA trên Ubuntu 20.04 . https://godbolt.org/ GCC 10.1 địa phương của tôi chưa nhận conceptra, vì vậy không chắc chắn điều gì đang xảy ra.


1

Có một số đơn giản tương đương với từ khóa này trong C ++?

Không.

Tùy thuộc vào những gì bạn đang cố gắng thực hiện, có thể có những sự thay thế đầy đủ (hoặc thậm chí tốt hơn).

Tôi đã xem qua một số mã STL (trên linux, tôi nghĩ đó là mã xuất phát từ việc triển khai SGI). Nó có "xác nhận khái niệm"; chẳng hạn, nếu bạn yêu cầu một kiểu hiểu *x++x, xác nhận khái niệm sẽ chứa mã đó trong hàm do-nothing (hoặc một cái gì đó tương tự). Nó đòi hỏi một số chi phí, vì vậy có thể là thông minh khi đặt nó trong một macro mà định nghĩa của nó phụ thuộc vào #ifdef debug.

Nếu mối quan hệ của lớp con thực sự là những gì bạn muốn biết, bạn có thể khẳng định trong hàm tạo T instanceof list(ngoại trừ "đánh vần" khác trong C ++). Bằng cách đó, bạn có thể kiểm tra theo cách của bạn ra khỏi trình biên dịch không thể kiểm tra nó cho bạn.


1

Không có từ khóa cho các kiểm tra loại như vậy, nhưng bạn có thể đặt một số mã trong đó ít nhất sẽ thất bại theo cách có trật tự:

(1) Nếu bạn muốn một mẫu hàm chỉ chấp nhận các tham số của lớp cơ sở X nhất định, hãy gán nó cho tham chiếu X trong hàm của bạn. (2) Nếu bạn muốn chấp nhận các hàm nhưng không phải là nguyên thủy hoặc ngược lại hoặc bạn muốn lọc các lớp theo các cách khác, hãy gọi hàm trợ giúp mẫu (trống) trong hàm của bạn chỉ được xác định cho các lớp bạn muốn chấp nhận.

Bạn cũng có thể sử dụng (1) và (2) trong các hàm thành viên của một lớp để buộc các loại kiểm tra này trên toàn bộ lớp.

Bạn có thể có thể đặt nó vào một số Macro thông minh để giảm bớt nỗi đau của bạn. :)


-2

Chà, bạn có thể tạo mẫu của bạn đọc một cái gì đó như thế này:

template<typename T>
class ObservableList {
  std::list<T> contained_data;
};

Tuy nhiên, điều này sẽ làm cho hạn chế ngầm định, cộng với việc bạn không thể cung cấp bất cứ thứ gì trông giống như một danh sách. Có nhiều cách khác để hạn chế các loại vùng chứa được sử dụng, ví dụ bằng cách sử dụng các kiểu trình vòng lặp cụ thể không tồn tại trong tất cả các vùng chứa nhưng một lần nữa, đây là một ẩn ý hơn là một hạn chế rõ ràng.

Theo hiểu biết tốt nhất của tôi, một cấu trúc sẽ phản ánh câu lệnh Java tới mức đầy đủ của nó không tồn tại trong tiêu chuẩn hiện tại.

Có nhiều cách để hạn chế các loại bạn có thể sử dụng bên trong một mẫu bạn viết bằng cách sử dụng các typedefs cụ thể bên trong mẫu của bạn. Điều này sẽ đảm bảo rằng việc biên dịch chuyên môn mẫu cho một loại không bao gồm typedef cụ thể đó sẽ thất bại, vì vậy bạn có thể hỗ trợ có chọn lọc / không hỗ trợ một số loại nhất định.

Trong C ++ 11, việc giới thiệu các khái niệm sẽ giúp việc này dễ dàng hơn nhưng tôi không nghĩ nó sẽ làm chính xác những gì bạn muốn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.