Các quy tắc cho mã thông báo “…” trong ngữ cảnh của các mẫu khác nhau là gì?


98

Trong C ++ 11 có các mẫu khác nhau như sau:

template< class T, class... Args >
unique_ptr<T> make_unique( Args&&... args )
{
    return unique_ptr<T>(new T(std::forward<Args>(args)...));
}

Có một số điều tò mò về điều này: Biểu thức std::forward<Args>(args)...sử dụng cả Argsargsnhưng chỉ một ...mã thông báo. Hơn nữa, std::forwardlà một hàm mẫu không biến thể chỉ lấy một tham số mẫu và một đối số. Các quy tắc cú pháp cho điều đó (đại khái) là gì? Làm thế nào nó có thể được khái quát?

Ngoài ra: Trong việc triển khai hàm, dấu chấm lửng ( ...) ở cuối biểu thức quan tâm. Có lý do gì mà trong danh sách đối số mẫu và danh sách tham số, dấu chấm lửng ở giữa không?


2
Tóm tắt về phần thứ hai: Khi "khai báo" một gói tham số mẫu hoặc gói tham số chức năng, nó ...xuất hiện trước khi số nhận dạng được đưa vào. Khi sử dụng một trong hai hoặc cả hai loại gói, phần ...mở rộng đứng sau mẫu biểu thức.
aschepler

Câu trả lời:


99

Trong ngữ cảnh của mẫu đa dạng, dấu chấm lửng ...được sử dụng để giải nén gói tham số mẫu nếu nó xuất hiện ở phía bên phải của một biểu thức (gọi mẫu biểu thức này trong giây lát). Quy tắc là bất kỳ mẫu nào ở phía bên trái của ...đều được lặp lại - các mẫu được giải nén ( bây giờ gọi chúng là biểu thức ) được phân tách bằng dấu phẩy ,.

Nó có thể được hiểu rõ nhất bằng một số ví dụ. Giả sử bạn có mẫu hàm này:

template<typename ...T>
void f(T ... args) 
{
   g( args... );        //pattern = args
   h( x(args)... );     //pattern = x(args)
   m( y(args...) );     //pattern = args (as argument to y())
   n( z<T>(args)... );  //pattern = z<T>(args)
}

Bây giờ nếu tôi gọi hàm này Tlà truyền dưới dạng {int, char, short}, thì mỗi lệnh gọi hàm được mở rộng thành:

g( arg0, arg1, arg2 );           
h( x(arg0), x(arg1), x(arg2) );
m( y(arg0, arg1, arg2) );
n( z<int>(arg0), z<char>(arg1), z<short>(arg2) );

Trong mã bạn đã đăng, hãy làm std::forwardtheo mẫu thứ tư được minh họa bằng n()lệnh gọi hàm.

Lưu ý sự khác biệt giữa x(args)...y(args...)trên!


Bạn cũng có thể sử dụng ...để khởi tạo một mảng như sau:

struct data_info
{
     boost::any  data;
     std::size_t type_size;
};

std::vector<data_info> v{{args, sizeof(T)}...}; //pattern = {args, sizeof(T)}

được mở rộng thành:

std::vector<data_info> v 
{ 
   {arg0, sizeof(int)},
   {arg1, sizeof(char)},
   {arg2, sizeof(short)}
};

Tôi vừa nhận ra một mẫu thậm chí có thể bao gồm bộ chỉ định truy cập, chẳng hạn như public, như được hiển thị trong ví dụ sau:

template<typename ... Mixins>
struct mixture : public Mixins ...  //pattern = public Mixins
{
    //code
};

Trong ví dụ này, mẫu được mở rộng thành:

struct mixture__instantiated : public Mixin0, public Mixin1, .. public MixinN  

Đó là, dẫn mixturexuất công khai từ tất cả các lớp cơ sở.

Hy vọng rằng sẽ giúp.


1
Về biểu thức được khớp; Nó phải là biểu thức lớn nhất có thể , phải không? Ví dụ x+args...nên được mở rộng thành x+arg0,x+arg1,x+arg2, không phải x+arg0,arg1,arg2.
bitmask

Vì vậy, ...áp dụng cho mọi thực thể có thể mở rộng trong mẫu.
Lightness Races ở Orbit

@bitmask: Có. x+args...nên mở rộng thành x+arg0,x+arg1,x+arg2, không x+arg0,arg1,arg2 .
Nawaz

1
@ Jarod42: Tôi sẽ cập nhật câu trả lời này sau khi C ++ 17 được phát hành.
Nawaz

3
@synther: sizeof...(T)không cần thiết ở đó. Bạn có thể đơn giản viết:int a[] = { ___ };
Nawaz

48

Phần sau được trích từ bài nói chuyện "Các mẫu đa dạng là Funadic" của Andrei Alexandrescu tại GoingNative 2012. Tôi có thể giới thiệu nó để có một phần giới thiệu tốt về các mẫu đa dạng.


Có hai điều người ta có thể làm với một gói đa dạng. Có thể áp dụng sizeof...(vs)để lấy số lượng phần tử và mở rộng nó.

Quy tắc mở rộng

Use            Expansion

Ts...          T1, ..., Tn
Ts&&...        T1&&, ..., Tn&&
x<Ts,Y>::z...  x<T1,Y>::z, ..., x<Tn,Y>::z
x<Ts&,Us>...   x<T1&,U1>, ..., x<Tn&,Un>
func(5,vs)...  func(5,v1), ..., func(5,vn)

Mở rộng tiến hành từ trong ra ngoài. Khi mở rộng hai danh sách trong bước khóa, chúng phải có cùng kích thước.

Các ví dụ khác:

gun(A<Ts...>::hun(vs)...);

Mở rộng tất cả Tstrong danh sách đối số mẫu Avà sau đó hàm hunđược mở rộng với tất cả vs.

gun(A<Ts...>::hun(vs...));

Mở rộng tất cả Tstrong danh sách đối số mẫu Avà tất cả vsdưới dạng đối số hàm cho hun.

gun(A<Ts>::hun(vs)...);

Mở rộng chức năng hunvới Tsvstrong bước khóa.

Ghi chú:

Tskhông phải là một loại và vskhông phải là một giá trị! Chúng là bí danh cho danh sách các loại / giá trị. Một trong hai danh sách có thể trống. Cả hai chỉ tuân theo những hành động cụ thể. Vì vậy, những điều sau đây là không thể:

typedef Ts MyList;  // error!
Ts var;             // error!
auto copy = vs;     // error!

Loci mở rộng

Đối số hàm

template <typename... Ts>
void fun(Ts... vs)

Danh sách trình khởi tạo

any a[] = { vs... };

Thông số cơ sở

template <typename... Ts>
struct C : Ts... {};
template <typename... Ts>
struct D : Box<Ts>... { /**/ };

Danh sách trình khởi tạo thành viên

// Inside struct D
template <typename... Us>
D(Us... vs) : Box<Ts>(vs)... {}

Danh sách đối số tạm thời

std::map<Ts...> m;

Sẽ chỉ biên dịch nếu có thể phù hợp với các đối số.

Chụp danh sách

template <class... Ts> void fun(Ts... vs) {
    auto g = [&vs...] { return gun(vs...); }
    g();
}

Danh sách thuộc tính

struct [[ Ts... ]] IAmFromTheFuture {};

Nó nằm trong đặc điểm kỹ thuật, nhưng vẫn chưa có thuộc tính nào có thể được thể hiện dưới dạng một kiểu.


Đẹp. Mặc dù vậy, các đối số của hàm bị loại ra khỏi các loci: P
Potatoswatter

@Potatoswatter Cảm ơn. Việc mở rộng trong danh sách đối số hàm không được đề cập.
typ1232,

@ typ1232 Xin lỗi vì đã chỉnh sửa, nhưng tôi cảm thấy rằng bài viết gốc của bạn quá gần với việc đạo văn. Btw, tôi cũng đã xem cuộc nói chuyện đó một thời gian trước - tuyệt vời.
Walter
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.