Là C ++ bối cảnh miễn phí hoặc nhạy cảm bối cảnh?


405

Tôi thường nghe tuyên bố rằng C ++ là một ngôn ngữ nhạy cảm theo ngữ cảnh. Lấy ví dụ sau:

a b(c);

Đây là một định nghĩa biến hoặc khai báo hàm? Điều đó phụ thuộc vào ý nghĩa của biểu tượng c. Nếu clà một biến , sau đó a b(c);định nghĩa một biến có tên bloại a. Nó được khởi tạo trực tiếp với c. Nhưng nếu clà một kiểu , sau đó a b(c);khai báo một hàm có tên blà a cvà trả về một a.

Nếu bạn tìm định nghĩa của các ngôn ngữ không ngữ cảnh, về cơ bản nó sẽ cho bạn biết rằng tất cả các quy tắc ngữ pháp phải có các cạnh bên trái bao gồm chính xác một ký hiệu không phải đầu cuối. Mặt khác, các ngữ pháp nhạy cảm theo ngữ cảnh, cho phép các chuỗi ký hiệu đầu cuối và không đầu cuối tùy ý ở phía bên trái.

Duyệt qua Phụ lục A của "Ngôn ngữ lập trình C ++", tôi không thể tìm thấy một quy tắc ngữ pháp duy nhất có bất kỳ thứ gì khác ngoài một biểu tượng không đầu cuối ở phía bên trái của nó. Điều đó có nghĩa là C ++ không có ngữ cảnh. (Tất nhiên, mọi ngôn ngữ không ngữ cảnh cũng nhạy cảm theo ngữ cảnh theo nghĩa các ngôn ngữ không ngữ cảnh tạo thành một tập hợp con của các ngôn ngữ nhạy ngữ cảnh, nhưng đó không phải là vấn đề.)

Vì vậy, C ++ không có ngữ cảnh hay nhạy cảm với bối cảnh?


12
@CarlNorum Vui lòng chỉ cho tôi một quy tắc ngữ pháp duy nhất của C ++ không bao gồm một ký hiệu không phải đầu cuối ở phía bên trái của nó và tôi sẽ ngay lập tức tin bạn.
dòng chảy

9
IIUC nó phụ thuộc một chút vào nơi bạn vẽ đường cho độ nhạy ngữ cảnh. Tôi nghĩ rằng tôi đã thấy mọi người lập luận rằng hầu hết tất cả các ngôn ngữ lập trình được nhập tĩnh đều nhạy cảm với ngữ cảnh, không phải vì bạn không thể xây dựng trình biên dịch thực tế cho chúng bằng các công cụ phân tích CFG, mà bởi vì các triển khai đó "gian lận" bằng cách phân tích một số chương trình không hợp lệ và chỉ từ chối chúng sau, trong khi kiểm tra loại. Vì vậy, nếu bạn coi các chương trình gõ sai không phải là ngôn ngữ (theo nghĩa CS, tức là một chuỗi), trình phân tích cú pháp nên chấp nhận, nhiều ngôn ngữ hơn C ++ nhạy cảm với ngữ cảnh.

6
@DeadMG: Không, bạn sai rồi. Không có "phân tích cú pháp" hay "ngữ nghĩa" trong lý thuyết ngôn ngữ chính thức, chỉ có "ngôn ngữ" là một chuỗi các chuỗi.
jpalecek

27
Không có câu trả lời cho đến nay đã thực sự giải quyết định nghĩa của bạn về "ngữ pháp không ngữ cảnh". Theo tôi, câu trả lời chính xác cho câu hỏi này hoặc trích dẫn một sản phẩm trong phụ lục A không phù hợp với định nghĩa của bạn hoặc chứng minh rằng định nghĩa của bạn không chính xác hoặc không đủ. Giữ vững lập trường của bạn!
Các cuộc đua nhẹ nhàng trong quỹ đạo

8
Xem ngữ pháp của D có thực sự không ngữ cảnh không? . Trên thực tế, tôi nghĩ mọi người ở đây nên đọc câu hỏi đó và câu trả lời của nó!
Các cuộc đua nhẹ nhàng trong quỹ đạo

Câu trả lời:


341

Dưới đây là phần trình diễn yêu thích (hiện tại) của tôi về lý do phân tích cú pháp C ++ (có thể) Turing-Complete , vì nó hiển thị một chương trình đúng về mặt cú pháp khi và chỉ khi một số nguyên cho trước là số nguyên tố.

Vì vậy, tôi khẳng định rằng C ++ không có ngữ cảnh cũng không nhạy cảm với ngữ cảnh .

Nếu bạn cho phép các chuỗi ký hiệu tùy ý ở cả hai mặt của bất kỳ sản phẩm nào, bạn sẽ tạo ra một ngữ pháp Loại 0 ("không giới hạn") trong hệ thống phân cấp Chomsky , mạnh hơn ngữ pháp nhạy cảm theo ngữ cảnh; ngữ pháp không giới hạn là Turing-Complete. Một ngữ pháp nhạy cảm theo ngữ cảnh (Loại 1) cho phép nhiều biểu tượng ngữ cảnh ở phía bên trái của sản phẩm, nhưng cùng một bối cảnh phải xuất hiện ở phía bên phải của sản phẩm (do đó có tên là "nhạy cảm ngữ cảnh"). [1] Các ngữ pháp nhạy cảm theo ngữ cảnh tương đương với các máy Turing giới hạn tuyến tính .

Trong chương trình ví dụ, tính toán nguyên tố có thể được thực hiện bằng máy Turing giới hạn tuyến tính, do đó nó không hoàn toàn chứng minh tính tương đương của Turing, nhưng phần quan trọng là trình phân tích cú pháp cần thực hiện tính toán để thực hiện phân tích cú pháp. Nó có thể là bất kỳ tính toán rõ ràng nào dưới dạng khởi tạo mẫu và có mọi lý do để tin rằng khởi tạo mẫu C ++ là Turing-Complete. Xem, ví dụ, bài viết năm 2003 của Todd L. Veldhuizen .

Bất kể, C ++ có thể được phân tích cú pháp bằng máy tính, do đó chắc chắn nó có thể được phân tích cú pháp bằng máy Turing. Do đó, một ngữ pháp không hạn chế có thể nhận ra nó. Trên thực tế, việc viết một ngữ pháp như vậy sẽ không thực tế, đó là lý do tại sao tiêu chuẩn không cố gắng làm như vậy. (Xem bên dưới.)

Vấn đề với "sự mơ hồ" của một số biểu hiện nhất định chủ yếu là cá trích đỏ. Để bắt đầu, sự mơ hồ là một tính năng của một ngữ pháp cụ thể, không phải là một ngôn ngữ. Ngay cả khi một ngôn ngữ có thể được chứng minh là không có ngữ pháp rõ ràng, nếu ngôn ngữ đó có thể được nhận ra bởi một ngữ pháp không ngữ cảnh, thì nó không có ngữ cảnh. Tương tự, nếu nó không thể được nhận ra bởi một ngữ pháp không ngữ cảnh nhưng nó có thể được nhận ra bởi một ngữ pháp nhạy cảm theo ngữ cảnh, nó nhạy cảm theo ngữ cảnh. Sự mơ hồ không liên quan.

Nhưng trong bất kỳ sự kiện nào, như dòng 21 (tức là auto b = foo<IsPrime<234799>>::typen<1>();) trong chương trình bên dưới, các biểu thức hoàn toàn không mơ hồ; chúng chỉ đơn giản được phân tích cú pháp khác nhau tùy thuộc vào ngữ cảnh. Trong cách diễn đạt đơn giản nhất của vấn đề, phạm trù cú pháp của một số định danh nhất định phụ thuộc vào cách chúng được khai báo (ví dụ: loại và hàm), có nghĩa là ngôn ngữ chính thức sẽ phải nhận ra thực tế là hai chuỗi có độ dài tùy ý trong cùng một chương trình là giống hệt nhau (khai báo và sử dụng). Điều này có thể được mô hình hóa bằng ngữ pháp "sao chép", đó là ngữ pháp nhận ra hai bản sao chính xác liên tiếp của cùng một từ. Thật dễ dàng để chứng minh với bổ đề bơmrằng ngôn ngữ này không có ngữ cảnh. Có thể sử dụng ngữ pháp nhạy với ngữ cảnh cho ngôn ngữ này và ngữ pháp Loại 0 được cung cấp trong câu trả lời cho câu hỏi này: /math/163830/context-sensitive-grammar-for-the- ngôn ngữ sao chép .

Nếu một người cố gắng viết một ngữ pháp nhạy cảm theo ngữ cảnh (hoặc không bị hạn chế) để phân tích C ++, thì nó hoàn toàn có thể lấp đầy vũ trụ bằng những nét vẽ nguệch ngoạc. Viết một máy Turing để phân tích C ++ sẽ là một công việc không thể bằng. Ngay cả việc viết một chương trình C ++ cũng khó, và theo như tôi biết thì không có gì được chứng minh là đúng. Đây là lý do tại sao tiêu chuẩn không cố gắng cung cấp một ngữ pháp chính thức hoàn chỉnh và tại sao nó chọn viết một số quy tắc phân tích cú pháp bằng tiếng Anh kỹ thuật.

Những gì trông giống như một ngữ pháp chính thức trong tiêu chuẩn C ++ không phải là định nghĩa chính thức hoàn chỉnh về cú pháp của ngôn ngữ C ++. Nó thậm chí không phải là định nghĩa chính thức hoàn chỉnh của ngôn ngữ sau khi tiền xử lý, có thể dễ dàng chính thức hóa hơn. (Tuy nhiên, đó không phải là ngôn ngữ: ngôn ngữ C ++ như được định nghĩa bởi tiêu chuẩn bao gồm bộ tiền xử lý và hoạt động của bộ tiền xử lý được mô tả bằng thuật toán vì sẽ rất khó để mô tả trong bất kỳ hình thức ngữ pháp nào. Nó nằm trong phần đó. của tiêu chuẩn nơi phân tách từ vựng được mô tả, bao gồm các quy tắc nơi nó phải được áp dụng nhiều lần.)

Các ngữ pháp khác nhau (hai ngữ pháp chồng chéo để phân tích từ vựng, một ngữ pháp diễn ra trước khi tiền xử lý và ngữ pháp khác, nếu cần, sau đó, cộng với ngữ pháp "cú pháp") được thu thập trong Phụ lục A, với ghi chú quan trọng này (nhấn mạnh thêm):

Tóm tắt cú pháp C ++ này nhằm mục đích hỗ trợ cho việc hiểu. Nó không phải là một tuyên bố chính xác của ngôn ngữ . Cụ thể, ngữ pháp được mô tả ở đây chấp nhận một siêu cấu trúc C ++ hợp lệ . Các quy tắc định hướng (6.8, 7.1, 10.2) phải được áp dụng để phân biệt biểu thức với khai báo. Hơn nữa, các quy tắc kiểm soát truy cập, sự mơ hồ và loại phải được sử dụng để loại bỏ các cấu trúc có giá trị về mặt cú pháp nhưng vô nghĩa.

Cuối cùng, đây là chương trình đã hứa. Dòng 21 đúng về mặt cú pháp khi và chỉ khi N in IsPrime<N>là số nguyên tố. Mặt khác, typenlà một số nguyên, không phải là một mẫu, do đó typen<1>()được phân tích cú pháp (typen<1)>()là không chính xác về mặt cú pháp vì ()không phải là một biểu thức hợp lệ về mặt cú pháp.

template<bool V> struct answer { answer(int) {} bool operator()(){return V;}};

template<bool no, bool yes, int f, int p> struct IsPrimeHelper
  : IsPrimeHelper<p % f == 0, f * f >= p, f + 2, p> {};
template<bool yes, int f, int p> struct IsPrimeHelper<true, yes, f, p> { using type = answer<false>; };
template<int f, int p> struct IsPrimeHelper<false, true, f, p> { using type = answer<true>; };

template<int I> using IsPrime = typename IsPrimeHelper<!(I&1), false, 3, I>::type;
template<int I>
struct X { static const int i = I; int a[i]; }; 

template<typename A> struct foo;
template<>struct foo<answer<true>>{
  template<int I> using typen = X<I>;
};
template<> struct foo<answer<false>>{
  static const int typen = 0;
};

int main() {
  auto b = foo<IsPrime<234799>>::typen<1>(); // Syntax error if not prime
  return 0;
}

[1] Để nói một cách kỹ thuật hơn, mọi sản phẩm trong một ngữ pháp nhạy cảm theo ngữ cảnh phải có dạng:

αAβ → αγβ

trong đó Akhông phải là một thiết bị đầu cuối và α, βcó thể là các chuỗi trống của các ký hiệu ngữ pháp và γlà một chuỗi không trống. (Ký hiệu ngữ pháp có thể là thiết bị đầu cuối hoặc không phải thiết bị đầu cuối).

Điều này có thể được đọc như A → γchỉ trong bối cảnh [α, β]. Trong ngữ pháp không có ngữ cảnh (Loại 2) αβphải trống.

Hóa ra bạn cũng có thể hạn chế ngữ pháp với hạn chế "đơn điệu", trong đó mọi sản phẩm phải có dạng:

α → βtrong đó |α| ≥ |β| > 0  ( |α|có nghĩa là "chiều dài của α")

Có thể chứng minh rằng tập hợp các ngôn ngữ được nhận biết bởi các ngữ pháp đơn điệu hoàn toàn giống với tập hợp các ngôn ngữ được các ngữ pháp nhạy cảm theo ngữ cảnh nhận ra và thường thì việc chứng minh dựa trên các ngữ pháp đơn điệu sẽ dễ dàng hơn. Do đó, khá phổ biến khi thấy "nhạy cảm theo ngữ cảnh" được sử dụng như thể nó có nghĩa là "đơn điệu".


27
Vì vậy, nó không chỉ nhạy cảm với ngữ cảnh mà còn có thể được thực hiện để phụ thuộc vào bất kỳ bối cảnh nào bạn có thể thể hiện trong các mẫu, đó là Turing-Complete.
Cún con

7
@mehrdad, OP nói "ngôn ngữ nhạy cảm ngữ cảnh", không phải ngữ pháp nhạy cảm ngữ cảnh. Sự mơ hồ là một tính năng của một ngữ pháp, không phải là một ngôn ngữ. Ngôn ngữ thực sự nhạy cảm với ngữ cảnh, nhưng không phải vì một ngữ pháp cụ thể cho nó là mơ hồ.
rici

2
Lưu ý rằng ví dụ của tôi không mơ hồ. Đó là một biểu hiện rõ ràng của một chương trình hợp lệ. Nếu bạn thay đổi giá trị trong dòng 21, nó có thể bị biến dạng. Nhưng không phải trường hợp nào cũng mơ hồ.
rici

5
Tôi có một nghi ngờ: Như bạn trình bày, kết quả của việc đánh giá mẫu có thể tạo ra sự khác biệt giữa một chương trình được định dạng tốt và không đúng định dạng. Đánh giá mẫu là hoàn thành. Vì vậy, sẽ không xác định chính xác liệu một chuỗi trong ngôn ngữ (C ++) có yêu cầu tính đầy đủ không? Như bạn nói, một ngôn ngữ nhạy cảm theo ngữ cảnh là "chỉ" một "máy tự động giới hạn tuyến tính", không phải là AFAIK hoàn chỉnh. Hoặc là đối số của bạn sử dụng các giới hạn mà tiêu chuẩn C ++ đặt ra cho một số điều bao gồm cả độ sâu đánh giá mẫu?

4
@AntonGolov: Phiên bản gốc của ví dụ đó của tôi đã làm điều đó (bạn có thể đạt được nó bằng cách đặt 0bên trong (), cho đơn giản), nhưng tôi nghĩ nó thú vị hơn theo cách này, bởi vì nó chứng minh rằng bạn cần ngay lập tức mẫu để nhận ra một chuỗi là một chương trình C ++ chính xác về mặt cú pháp. Nếu cả hai nhánh biên dịch, thì tôi sẽ phải làm việc chăm chỉ hơn để tranh luận rằng sự khác biệt là "ngữ nghĩa". Thật kỳ lạ, mặc dù tôi thường thử thách để xác định "cú pháp", không ai đã từng đưa ra một định nghĩa về "ngữ nghĩa" không phải là "thứ mà tôi không nghĩ là cú pháp" :)
rici

115

Đầu tiên, bạn quan sát một cách đúng đắn không có quy tắc nhạy cảm ngữ cảnh nào trong ngữ pháp ở cuối tiêu chuẩn C ++, do đó ngữ pháp không ngữ cảnh.

Tuy nhiên, ngữ pháp đó không mô tả chính xác ngôn ngữ C ++, vì nó tạo ra các chương trình không phải C ++ như

int m() { m++; }

hoặc là

typedef static int int;

Ngôn ngữ C ++ được định nghĩa là "tập hợp các chương trình C ++ được hình thành tốt" không có ngữ cảnh (có thể chỉ ra rằng chỉ yêu cầu các biến được khai báo khiến nó trở nên như vậy). Về mặt lý thuyết, bạn có thể viết các chương trình hoàn chỉnh Turing trong các mẫu và làm cho chương trình không thành công dựa trên kết quả của chúng, nó thậm chí không nhạy cảm theo ngữ cảnh.

Bây giờ, những người (không biết gì) (thường không phải là nhà lý thuyết ngôn ngữ, nhưng người thiết kế trình phân tích cú pháp) thường sử dụng "không ngữ cảnh" trong một số ý nghĩa sau đây

  • mơ hồ
  • không thể được phân tích cú pháp bằng Bison
  • không phải LL (k), LR (k), LALR (k) hoặc bất kỳ lớp ngôn ngữ nào do trình phân tích cú pháp xác định mà họ đã chọn

Ngữ pháp ở mặt sau của tiêu chuẩn không thỏa mãn các loại này (nghĩa là mơ hồ, không phải LL (k) ...) vì vậy ngữ pháp C ++ là "không ngữ cảnh" đối với chúng. Và theo một nghĩa nào đó, họ đúng là rất khó để tạo ra một trình phân tích cú pháp C ++ hoạt động.

Lưu ý rằng các thuộc tính được sử dụng ở đây chỉ được kết nối yếu với các ngôn ngữ không ngữ cảnh - sự mơ hồ không liên quan gì đến tính nhạy cảm ngữ cảnh (trên thực tế, các quy tắc nhạy cảm theo ngữ cảnh thường giúp phân biệt sản phẩm), hai ngôn ngữ kia chỉ là tập hợp con của ngữ cảnh ngôn ngữ miễn phí. Và phân tích cú pháp các ngôn ngữ không ngữ cảnh không phải là một quá trình tuyến tính (mặc dù phân tích cú pháp các ngôn ngữ xác định là).


7
ambiguity doesn't have anything to do with context-sensitivityĐây cũng là trực giác của tôi, vì vậy tôi rất vui khi thấy ai đó (a) đồng ý và (b) giải thích nó ở nơi tôi không thể. Tôi tin rằng nó không đủ tiêu chuẩn cho tất cả các lập luận dựa trên a b(c);và thỏa mãn một phần câu hỏi ban đầu có tiền đề là "nghe nói" về tính nhạy cảm ngữ cảnh là do sự mơ hồ ... đặc biệt là khi ngữ pháp thực sự không có sự mơ hồ ngay cả trong MVP.
Các cuộc đua nhẹ nhàng trong quỹ đạo

6
@KonradRudolph: Điều mà tiêu chuẩn nói là "Có một đại lượng được xác định theo triển khai chỉ định giới hạn về tổng độ sâu của đệ quy đệ quy, có thể liên quan đến nhiều hơn một mẫu. Kết quả của phép đệ quy vô hạn trong khởi tạo không được xác định." (14.7.1p15) Tôi giải thích rằng điều đó có nghĩa là việc triển khai không bắt buộc phải hiểu mọi chương trình c ++ hợp lệ, không phải các chương trình có độ sâu đệ quy quá lớn là không hợp lệ. Những người duy nhất được đánh dấu là không hợp lệ là những người có độ sâu đệ quy vô hạn.
rici

3
@KonradRudolph: Tôi tranh luận rằng đó là "tài liệu tham khảo chung". Việc tôi đọc bài báo khá phức tạp đó và không hiểu nó đủ để đưa ra thực tế nhỏ này là đủ để chứng minh điều đó. Không phải như bạn nói điều gì đó như "máy tính thường sử dụng điện" hay "bit có thể đúng hoặc sai".
Các cuộc đua nhẹ nhàng trong quỹ đạo

3
Nếu sự thật này thực sự được biết đến rộng rãi, tôi nghĩ rằng việc tìm một tài liệu tham khảo về nó sẽ dễ dàng hơn nhiều so với việc tranh luận về việc có nên cung cấp hay không. Chưa kể mang tính xây dựng.
Phường Samuel Edwin

5
Theo như tôi có thể nói, @Konrad đã nhầm khi nói rằng "Bối cảnh nhạy cảm tương đương với Turing hoàn chỉnh". (ít nhất, anh ta là nếu anh ta biểu thị "đệ quy đệ quy" bằng "Turing hoàn thành"), và từ đó không thể nhận ra sai lầm này. Dưới đây là tài liệu tham khảo cho mối quan hệ bao gồm tập hợp thích hợp có liên quan ở đây: en.wikipedia.org/wiki/Chomsky_hierarchy
pnkfelix

61

Đúng. Biểu thức sau đây có thứ tự hoạt động khác nhau tùy thuộc vào loại bối cảnh được giải quyết :

Chỉnh sửa: Khi thứ tự hoạt động thực tế khác nhau, việc sử dụng trình biên dịch "thông thường" sẽ phân tích cú pháp AST không được trang trí trước khi trang trí nó (truyền thông tin loại) rất khó khăn. Những điều nhạy cảm bối cảnh khác được đề cập là "khá dễ dàng" so với điều này (không phải việc đánh giá mẫu hoàn toàn dễ dàng).

#if FIRST_MEANING
   template<bool B>
   class foo
   { };
#else
   static const int foo = 0;
   static const int bar = 15;
#endif

Theo dõi bởi:

static int foobar( foo < 2 ? 1 < 1 : 0 > & bar );

Tại sao vấn đề đó không thể được giải quyết như đối với C, bằng cách nhớ các định nghĩa loại nào trong phạm vi?
Blaisorblade

1
@Blaisorblade: Một cách để làm cho trình biên dịch "sạch" là tách các tác vụ thành các bước độc lập trong chuỗi, chẳng hạn như tạo một cây phân tích từ đầu vào theo sau là một bước phân tích kiểu. C ++ buộc bạn phải 1) hợp nhất các bước này thành một hoặc 2) phân tích tài liệu theo cả hai / tất cả các diễn giải có thể, và cho phép các giai đoạn phân giải loại thu hẹp nó thành giải thích chính xác.
Sam Harwell

@ 280Z28: đồng ý, nhưng đó cũng là trường hợp của C; Tôi nghĩ rằng một câu trả lời tốt cho câu hỏi này sẽ cho thấy tại sao C ++ kém hơn C. Luận án tiến sĩ được liên kết ở đây thực hiện điều đó: stackoverflow.com/a/243447/53974
Blaisorblade

26

Để trả lời câu hỏi của bạn, bạn cần phân biệt hai câu hỏi khác nhau.

  1. Cú pháp đơn thuần của hầu hết mọi ngôn ngữ lập trình là không có ngữ cảnh. Thông thường, nó được đưa ra dưới dạng Backus-Naur mở rộng hoặc ngữ pháp không ngữ cảnh.

  2. Tuy nhiên, ngay cả khi một chương trình phù hợp với ngữ pháp không ngữ cảnh được xác định bởi ngôn ngữ lập trình, nó không nhất thiết phải là một chương trình hợp lệ . Có nhiều chương trình giáo dục phi ngữ cảnh mà một chương trình phải đáp ứng để trở thành một chương trình hợp lệ. Ví dụ, thuộc tính đơn giản nhất như vậy là phạm vi của các biến.

Để kết luận, việc C ++ có không ngữ cảnh hay không phụ thuộc vào câu hỏi bạn hỏi.


5
Thật thú vị khi lưu ý rằng bạn thường phải đặt mức "cú pháp đơn thuần" thấp hơn mức bạn mong đợi, để có được CFG cho ngôn ngữ lập trình của bạn. Lấy C, ví dụ. Bạn có thể nghĩ rằng quy tắc ngữ pháp cho một khai báo biến đơn giản trong C sẽ là VARDECL : TYPENAME IDENTIFIER, nhưng bạn không thể có điều đó, bởi vì bạn không thể phân biệt tên loại với các mã định danh khác ở cấp độ CF. Một ví dụ khác: ở cấp độ CF, bạn không thể quyết định phân tích cú pháp a*bdưới dạng khai báo biến ( bcủa con trỏ kiểu thành a) hay dưới dạng phép nhân.
LaC

2
@LaC: Vâng, cảm ơn vì đã chỉ ra điều này! Nhân tiện, tôi chắc chắn rằng có một thuật ngữ kỹ thuật được sử dụng phổ biến hơn cho cú pháp đơn thuần . Có ai đúng thuật ngữ?
Dan

4
@Dan: những gì bạn đang nói là một xấp xỉ của ngôn ngữ được đưa ra bởi một số ngữ pháp không ngữ cảnh. Tất nhiên một định nghĩa gần đúng như vậy là không có định nghĩa. Đây là ý nghĩa trong đó "cú pháp" thường được sử dụng khi thảo luận về ngôn ngữ lập trình.
rebierpost

13

Bạn có thể muốn xem The Design & Evolution của C ++ , bởi Bjarne Stroustrup. Trong đó, ông mô tả các vấn đề của mình khi cố gắng sử dụng yacc (hoặc tương tự) để phân tích phiên bản đầu của C ++ và muốn thay vào đó ông sử dụng dòng dõi đệ quy.


Ồ cảm ơn nhé. Tôi tự hỏi liệu có thực sự có ý nghĩa khi nghĩ về việc sử dụng bất cứ thứ gì mạnh hơn CFG để phân tích bất kỳ ngôn ngữ nhân tạo nào.
Dervin Thunk

Cuốn sách tuyệt vời để hiểu về các vấn đề của C ++. Tôi khuyên bạn và Lippman's Inside the C ++ Object Model để hiểu cách C ++ hoạt động. Mặc dù cả hai có một chút ngày, họ vẫn đọc tốt.
Matt Giá

"Meta-S" là một công cụ phân tích cú pháp theo ngữ cảnh của Quinn Tyler Jackson. Tôi đã không sử dụng nó, nhưng anh ấy kể một câu chuyện ấn tượng. Kiểm tra nhận xét của anh ấy trong comp.compilers và xem rnapude.com/MetaS%20d xác định.htmlm
Ira Baxter

@IraBaxter: x-ref của bạn là MIA ngày hôm nay - và các tài liệu tham khảo chắc chắn về phần mềm dường như khó nắm bắt (tìm kiếm của Google không cung cấp bất kỳ khách hàng tiềm năng tốt nào, với 'site: rnapude.com meta-s' hoặc 'quinn jackson meta- s '; có bit và miếng, nhưng meta-s.com dẫn đến một trang web không có thông tin chẳng hạn).
Jonathan Leffler

@Jonathan: được một lúc, mới nhận thấy khiếu nại của bạn. Tại sao liên kết là xấu, tôi nghĩ rằng nó là tốt khi tôi viết nó. Quinn đã từng khá tích cực trong comp.compilers. Google dường như đang trở nên không ổn định, đây là tất cả những gì tôi có thể tìm thấy: Groups.google.com/group/comp.compilers/browse_thread/thread/ trộm IIRC, anh ấy đã đăng ký quyền đối với MetaS cho một số trang phục ở Hawaii để tiếp thị lại. Cho rằng điều này kỳ lạ đến mức nào, IMHO đây đang ký lệnh tử hình. Nghe có vẻ như một kế hoạch rất thông minh.
Ira Baxter

12

Yeah C ++ là bối cảnh nhạy cảm, rất nhạy cảm bối cảnh. Bạn không thể xây dựng cây cú pháp bằng cách phân tích cú pháp tệp bằng cách sử dụng trình phân tích cú pháp miễn phí ngữ cảnh vì trong một số trường hợp, bạn cần biết biểu tượng từ kiến ​​thức trước để quyết định (nghĩa là xây dựng bảng biểu tượng trong khi phân tích cú pháp).

Ví dụ đầu tiên:

A*B;

Đây có phải là một biểu thức nhân?

HOẶC LÀ

Đây có phải là một tuyên bố của Bbiến là một con trỏ của loại A?

Nếu A là một biến, thì đó là một biểu thức, nếu A là kiểu, đó là khai báo con trỏ.

Ví dụ thứ hai:

A B(bar);

Đây có phải là một nguyên mẫu hàm lấy một đối số của barloại?

HOẶC LÀ

Đây có phải là biến khai báo Bcủa kiểu Avà gọi hàm tạo của A với barhằng số là bộ khởi tạo không?

Bạn cần biết lại barlà một biến hoặc một loại từ bảng ký hiệu.

Ví dụ thứ ba:

class Foo
{
public:
    void fn(){x*y;}
    int x, y;
};

Đây là trường hợp khi xây dựng bảng ký hiệu trong khi phân tích cú pháp không giúp ích gì vì việc khai báo x và y xuất hiện sau định nghĩa hàm. Vì vậy, trước tiên bạn cần quét qua định nghĩa lớp và xem xét các định nghĩa phương thức trong lần truyền thứ hai, để nói x * y là một biểu thức và không phải là khai báo con trỏ hay bất cứ điều gì.


1
A B();là một khai báo hàm ngay cả trong một định nghĩa hàm. Tìm kiếm hầu hết các phân tích vexing ...
AProgrammer

"Bạn không thể xây dựng cây cú pháp bằng cách phân tích cú pháp qua tệp" FALSE. Xem câu trả lời của tôi.
Ira Baxter

10

C ++ được phân tích cú pháp với trình phân tích cú pháp GLR. Điều đó có nghĩa là trong quá trình phân tích mã nguồn, trình phân tích cú pháp có thể gặp phải sự mơ hồ nhưng nó sẽ tiếp tục và quyết định quy tắc ngữ pháp nào sẽ sử dụng sau này .

nhìn cũng được

Tại sao C ++ không thể được phân tích cú pháp bằng trình phân tích cú pháp LR (1)?


Hãy nhớ rằng ngữ pháp không ngữ cảnh không thể mô tả TẤT CẢ các quy tắc của cú pháp ngôn ngữ lập trình. Ví dụ, ngữ pháp thuộc tính được sử dụng để kiểm tra tính hợp lệ của một loại biểu thức.

int x;
x = 9 + 1.0;

Bạn không thể mô tả quy tắc sau với ngữ pháp không ngữ cảnh: Phía bên phải của bài tập phải cùng loại với phía bên tay trái.


4
Hầu hết các trình phân tích cú pháp C ++ không sử dụng công nghệ phân tích cú pháp GLR. GCC không. Một số làm. Xem semanticdesigns.com/ Products / FrontEnds / CppFrontEnd.html để biết điều đó.
Ira Baxter

10

Tôi có cảm giác rằng có một số nhầm lẫn giữa định nghĩa chính thức về "nhạy cảm theo ngữ cảnh" và việc sử dụng "nhạy cảm theo ngữ cảnh" không chính thức. Các cựu có một ý nghĩa được xác định rõ. Cái sau được sử dụng để nói "bạn cần ngữ cảnh để phân tích cú pháp đầu vào".

Điều này cũng được hỏi ở đây: Nhạy cảm với bối cảnh so với sự mơ hồ .

Đây là một ngữ pháp không ngữ cảnh:

<a> ::= <b> | <c>
<b> ::= "x"
<c> ::= "x"

Điều này không rõ ràng, vì vậy để phân tích đầu vào "x", bạn cần một số ngữ cảnh (hoặc sống với sự mơ hồ hoặc phát ra "Cảnh báo: E8271 - Đầu vào không rõ ràng trong dòng 115"). Nhưng nó chắc chắn không phải là một ngữ pháp nhạy cảm ngữ cảnh.


Làm thế nào để có nhiều biểu tượng ở phía bên trái của sản xuất giải quyết vấn đề này? Tôi không nghĩ câu trả lời này là trả lời câu hỏi.
dùng541686

1
Câu trả lời của tôi là để đáp lại câu đầu tiên: "Tôi thường nghe tuyên bố rằng C ++ là một ngôn ngữ nhạy cảm theo ngữ cảnh". Nếu những tuyên bố đó sử dụng biểu thức "nhạy cảm theo ngữ cảnh" một cách không chính thức, thì không có vấn đề gì. Tôi không nghĩ rằng C ++ chính thức nhạy cảm theo ngữ cảnh.
Omri Barel

Tôi nghĩ rằng C ++ chính thức bối cảnh nhạy cảm, nhưng vấn đề tôi đang gặp là tôi không hiểu làm thế nào một ngữ pháp context-sensitive sẽ có bất kỳ thành công hơn ở phân tích cú pháp C ++ hơn một CFG sẽ.
dùng541686

6

Không có ngôn ngữ giống như Algol là không có ngữ cảnh, bởi vì chúng có các quy tắc ràng buộc các biểu thức và câu lệnh mà định danh có thể xuất hiện dựa trên loại của chúng và vì không có giới hạn về số lượng câu lệnh có thể xảy ra giữa khai báo và sử dụng.

Giải pháp thông thường là viết một trình phân tích cú pháp không ngữ cảnh thực sự chấp nhận một siêu chương trình hợp lệ và đặt các phần nhạy cảm theo ngữ cảnh trong mã "ngữ nghĩa" ad hoc gắn liền với các quy tắc.

C ++ vượt xa điều này, nhờ hệ thống mẫu Turing-Complete. Xem Stack Overflow Câu 794015 .




5

Nó là ngữ cảnh nhạy cảm, vì a b(c);có hai phân tích cú pháp hợp lệ - khai báo và biến. Khi bạn nói "Nếu clà một loại", đó là bối cảnh, ngay tại đó và bạn đã mô tả chính xác mức độ nhạy cảm của C ++ với nó. Nếu bạn không có bối cảnh "Cái gì c?" bạn không thể phân tích cú pháp này một cách rõ ràng.

Ở đây, bối cảnh được thể hiện trong việc lựa chọn mã thông báo - trình phân tích cú pháp đọc mã định danh dưới dạng mã thông báo nếu nó đặt tên một loại. Đây là độ phân giải đơn giản nhất và tránh được nhiều sự phức tạp của việc nhạy cảm với bối cảnh (trong trường hợp này).

Chỉnh sửa: Tất nhiên, có nhiều vấn đề về độ nhạy ngữ cảnh, tôi chỉ tập trung vào vấn đề bạn đã trình bày. Mẫu đặc biệt khó chịu cho việc này.


1
Ngoài ra a<b<c>>d, phải không? (Ví dụ của bạn thực sự là một tác phẩm kinh điển từ C , trong đó nó là vật cản duy nhất để không có ngữ cảnh.)
Kerrek SB

Đó là nhiều hơn một vấn đề lexing, tôi nghĩ. Nhưng chắc chắn là trong cùng một thể loại, vâng.
Cún con

2
Người hỏi không hỏi làm thế nào nó nhiều bối cảnh nhạy cảm hơn so với C, chỉ để cho thấy rằng nó là bối cảnh nhạy cảm.
Cún con

Vì vậy, .. C ++ bối cảnh nhạy cảm hơn so với C?
Kerrek SB

2
@DeadMG Tôi không nghĩ bạn đang trả lời câu hỏi (Tôi cũng không nghĩ mình là ai). Làm thế nào để có các thiết bị đầu cuối ở phía bên trái của một sản xuất giải quyết vấn đề này?
dùng541686

5

Các sản phẩm trong tiêu chuẩn C ++ được viết không có ngữ cảnh, nhưng như chúng ta đều biết không thực sự xác định chính xác ngôn ngữ. Một số điều mà hầu hết mọi người coi là sự mơ hồ trong ngôn ngữ hiện tại (tôi tin) có thể được giải quyết rõ ràng với một ngữ pháp nhạy cảm theo ngữ cảnh.

Để có ví dụ rõ ràng nhất, chúng ta hãy xem xét Phân tích cú pháp nhiều nhất : int f(X);. Nếu Xlà một giá trị, thì điều này định nghĩa flà một biến sẽ được khởi tạo với X. Nếu Xlà một kiểu, nó định nghĩa flà một hàm lấy một tham số kiểu duy nhất X.

Nhìn vào điều đó từ quan điểm ngữ pháp, chúng ta có thể xem nó như thế này:

A variable_decl ::= <type> <identifier> '(' initializer ')' ';'

B function_decl ::= <type> <identifier> '(' param_decl ')' ';'

A ::= [declaration of X as value]
B ::= [declaration of X as type]

Tất nhiên, để hoàn toàn chính xác, chúng ta cần thêm một số "nội dung" bổ sung cho khả năng can thiệp khai báo của các loại khác (ví dụ, cả A và B nên thực sự là "khai báo bao gồm khai báo X là ..." , hoặc một cái gì đó theo thứ tự đó).

Điều này vẫn khá khác với một CSG điển hình mặc dù (hoặc ít nhất là những gì tôi nhớ về họ). Điều này phụ thuộc vào bảng biểu tượng được xây dựng - phần đặc biệt nhận dạng Xlà một loại hoặc giá trị, không chỉ một số loại câu lệnh trước đó, mà là loại câu lệnh chính xác cho ký hiệu / mã định danh đúng.

Như vậy, tôi phải thực hiện một số tìm kiếm để chắc chắn, nhưng phỏng đoán ngay lập tức của tôi là điều này không thực sự đủ điều kiện là CSG, ít nhất là thuật ngữ thường được sử dụng.


Các sản phẩm (không ngữ cảnh) xác định phân tích cú pháp nhiều nhất đủ tốt để nó có thể được phân tích cú pháp bởi một công cụ phân tích cú pháp ngữ cảnh miễn phí. Điều đó làm trì hoãn vấn đề quyết định nhiều cách hiểu nào là hợp lệ cho đến khi hoàn thành phân tích cú pháp, nhưng điều đó chỉ làm cho kỹ thuật của trình phân tích cú pháp và trình phân giải tên dễ dàng hơn, bởi vì chúng được mô đun hóa thay vì rối như trong các trình phân tích cú pháp C ++ thông thường. Xem AST để biết hầu hết phân tích cú pháp: stackoverflow.com/questions/17388771/ory
Ira Baxter

5

Trường hợp đơn giản nhất của ngữ pháp không ngữ cảnh liên quan đến phân tích cú pháp các biểu thức liên quan đến các mẫu.

a<b<c>()

Điều này có thể phân tích cú pháp

template
   |
   a < expr > ()
        |
        <
      /   \
     b     c

Hoặc là

 expr
   |
   <
 /   \
a   template
     |
     b < expr > ()
          |
          c

Hai AST chỉ có thể được định nghĩa bằng cách kiểm tra khai báo 'a' - AST cũ nếu 'a' là mẫu hoặc nếu không.


Tôi tin rằng C ++ 11 bắt buộc phải diễn giải sau và bạn phải thêm parens để chọn tham gia vào phiên bản trước.
Joseph Garvin

1
@JosephGarvin, không. Các lệnh C ++ <phải là dấu ngoặc nếu có thể (ví dụ: nó tuân theo một mã định danh đặt tên cho mẫu). C ++ 11 đã thêm yêu cầu >và ký tự đầu tiên >>được hiểu là dấu ngoặc gần nếu việc sử dụng đó là hợp lý. Điều này ảnh hưởng sự phân tích của a<b>c>nơi alà một mẫu nhưng không có tác dụng trên a<b<c>.
rici

@aaron: làm thế nào đơn giản hơn a();(đó là expr.callhoặc expr.type.conv)?
rici

@rici: Rất tiếc, tôi không nhận ra nó không đối xứng.
Joseph Garvin

5
Bạn đang mô tả sự mơ hồ , hoặc nhạy cảm bối cảnh?
corazza

4

Các mẫu C ++ đã được chứng minh là Turing Mạnh mẽ. Mặc dù không phải là một tài liệu tham khảo chính thức, đây là một nơi để xem xét về vấn đề đó:

http://cpptruths.blogspot.com/2005/11/c-temsheet-are-turing-complete.html

Tôi sẽ mạo hiểm đoán (cũ như một bằng chứng CACM dân gian và súc tích cho thấy ALGOL trong thập niên 60 không thể được CFG nhắc lại) và nói rằng C ++ chỉ có thể được phân tích chính xác bởi CFG. CFG, kết hợp với các cơ chế TP khác nhau trong một lần vượt qua cây hoặc trong các sự kiện giảm - đây là một câu chuyện khác. Nói chung, do Sự cố dừng, tồn tại một số chương trình C ++ không thể hiển thị là đúng / không chính xác nhưng dù sao cũng đúng / không chính xác.

{PS- Là tác giả của Meta-S (được đề cập bởi một số người ở trên) - Tôi có thể chắc chắn nhất nói rằng Thothic không bị phát hiện, cũng không phải là phần mềm có sẵn miễn phí. Có lẽ tôi đã thông báo phiên bản phản hồi này để không bị xóa hoặc bỏ phiếu xuống -3.}


3

C ++ không phải là bối cảnh miễn phí. Tôi đã học nó một thời gian trước trong bài giảng trình biên dịch. Một tìm kiếm nhanh đã đưa ra liên kết này, trong đó phần "Cú pháp hoặc ngữ nghĩa" giải thích tại sao C và C ++ không có ngữ cảnh:

Wikipedia Talk: Ngữ pháp không ngữ cảnh

Trân trọng,
Ovanes


2

Rõ ràng, nếu bạn lấy nguyên văn câu hỏi, gần như tất cả các ngôn ngữ có mã định danh đều nhạy cảm với ngữ cảnh.

Bạn cần biết một mã định danh có phải là tên loại (tên lớp, tên được giới thiệu bởi typedef, tham số mẫu tên), tên mẫu hoặc một số tên khác để có thể sử dụng chính xác một số cách sử dụng mã định danh. Ví dụ:

x = (name)(expression);

là một kiểu đúc nếu namelà tên kiểu và gọi hàm nếu namelà tên hàm. Một trường hợp khác là cái gọi là "phân tích vexing nhất" trong đó không thể phân biệt định nghĩa biến và khai báo hàm (có một quy tắc nói rằng đó là khai báo hàm).

Khó khăn đó đã giới thiệu sự cần thiết typenametemplatevới các tên phụ thuộc. Phần còn lại của C ++ không nhạy cảm theo ngữ cảnh như tôi biết (nghĩa là có thể viết một ngữ pháp miễn phí ngữ cảnh cho nó).


2

Meta-S "là một công cụ phân tích cú pháp nhạy cảm theo ngữ cảnh của Quinn Tyler Jackson. Tôi chưa sử dụng nó, nhưng anh ấy đã kể một câu chuyện ấn tượng. - Ira Baxter ngày 25 tháng 7 lúc 10:42

Liên kết chính xác là phân tích cú pháp

Meta-S là tài sản của một công ty không còn tồn tại tên là Thothic. Tôi có thể gửi một bản sao miễn phí của Meta-S cho bất kỳ ai quan tâm và tôi đã sử dụng nó trong nghiên cứu phân tích rna. Xin lưu ý "ngữ pháp giả" bao gồm trong các thư mục ví dụ được viết bởi một người không tin sinh học, lập trình viên chưa trưởng thành và về cơ bản không hoạt động. Ngữ pháp của tôi có một cách tiếp cận khác và hoạt động khá tốt.


Đây thực sự là một phát hiện thú vị.
Dervin Thunk

0

Một vấn đề lớn ở đây là các thuật ngữ "không ngữ cảnh" và "nhạy cảm với bối cảnh" là một chút không trực quan trong khoa học máy tính. Đối với C ++, độ nhạy ngữ cảnh trông rất giống với sự mơ hồ, nhưng điều đó không nhất thiết đúng trong trường hợp chung.

Trong C / ++, một câu lệnh if chỉ được phép bên trong một thân hàm. Điều đó dường như sẽ làm cho nó nhạy cảm theo ngữ cảnh, phải không? Ồ không. Các ngữ pháp không có ngữ cảnh thực sự không cần tài sản nơi bạn có thể lấy ra một số dòng mã và xác định xem nó có hợp lệ không. Đó thực sự không phải là những gì có nghĩa là bối cảnh. Nó thực sự chỉ là một nhãn hiệu mơ hồ ngụ ý một cái gì đó liên quan đến những gì nó nghe như.

Bây giờ, nếu một câu lệnh trong một thân hàm được phân tích cú pháp khác nhau tùy thuộc vào một cái gì đó được xác định bên ngoài tổ tiên ngữ pháp ngay lập tức (ví dụ: một định danh mô tả một loại hoặc biến), như trong a * b;trường hợp, trên thực tế, nó nhạy cảm với ngữ cảnh. Không có sự mơ hồ thực tế ở đây; nó sẽ được phân tích cú pháp dưới dạng khai báo của một con trỏ nếu alà kiểu và phép nhân.

Trở nên nhạy cảm với bối cảnh không nhất thiết có nghĩa là "khó phân tích". C thực sự không khó lắm vì a * b;"sự mơ hồ" khét tiếng có thể được giải quyết bằng một bảng biểu tượng có chứa typedefs gặp phải trước đó. Nó không yêu cầu bất kỳ khởi tạo mẫu tùy ý nào (đã được chứng minh là Turing Complete) để giải quyết trường hợp đó giống như C ++. Thật sự không thể viết một chương trình C sẽ không biên dịch trong một khoảng thời gian hữu hạn mặc dù nó có cùng độ nhạy ngữ cảnh như C ++ thực hiện.

Python (và các ngôn ngữ nhạy cảm với khoảng trắng khác) cũng phụ thuộc vào ngữ cảnh, vì nó yêu cầu trạng thái trong từ vựng để tạo ra các mã thông báo thụt lề và suy diễn, nhưng điều đó không làm cho nó khó phân tích hơn ngữ pháp LL-1 thông thường. Nó thực sự sử dụng một trình tạo trình phân tích cú pháp, đó là một phần lý do tại sao Python có các thông báo lỗi cú pháp không chính xác như vậy. Điều quan trọng cần lưu ý ở đây là không có "sự mơ hồ" như a * b;vấn đề trong Python, đưa ra một ví dụ cụ thể về ngôn ngữ nhạy cảm theo ngữ cảnh mà không có ngữ pháp "mơ hồ" (như đã đề cập trong đoạn đầu tiên).


-4

Câu trả lời này nói rằng C ++ không có ngữ cảnh ... có một hàm ý (không phải bởi người trả lời) rằng nó không thể được phân tích cú pháp và câu trả lời đưa ra một ví dụ mã khó tạo ra chương trình C ++ không hợp lệ nếu một hằng số nhất định không phải là một số nguyên tố.

Như những người khác đã quan sát, câu hỏi về việc liệu ngôn ngữ có nhạy cảm với ngữ cảnh / miễn phí hay không khác với cùng một câu hỏi về một ngữ pháp cụ thể.

Để đặt câu hỏi về khả năng phân tích cú pháp để nghỉ ngơi, tôi đưa ra bằng chứng thực nghiệm rằng có ngữ pháp không ngữ cảnh cho C ++, có thể được sử dụng để tạo AST cho phân tích cú pháp không có ngữ cảnh của văn bản nguồn bằng cách thực sự phân tích nó bằng GLR hiện có công cụ dựa trên -parser được điều khiển bởi một ngữ pháp rõ ràng.

Vâng, nó thành công bằng cách "chấp nhận quá nhiều"; không phải tất cả mọi thứ nó chấp nhận là một chương trình C ++ hợp lệ, đó là lý do tại sao nó được theo dõi với các kiểm tra bổ sung (kiểm tra loại). Và có, trình kiểm tra loại có thể gặp vấn đề về tính toán. Trong các công cụ thực hành không có vấn đề này; nếu mọi người viết chương trình như vậy, không ai trong số họ sẽ biên dịch. (Tôi nghĩ rằng tiêu chuẩn thực sự đặt ra giới hạn về số lượng tính toán mà bạn có thể thực hiện khi mở ra một mẫu, vì vậy trên thực tế tính toán thực sự là hữu hạn nhưng có lẽ khá lớn).

Nếu ý của bạn là, hãy xác định xem chương trình nguồn có phải là thành viên của tập hợp các chương trình nguồn C ++ hợp lệ hay không , thì tôi sẽ đồng ý rằng vấn đề khó hơn rất nhiều. Nhưng nó không phải là phân tích cú pháp đó là vấn đề.

Công cụ giải quyết vấn đề này bằng cách tách phân tích cú pháp khỏi kiểm tra kiểu chương trình được phân tích cú pháp. (Trong trường hợp có nhiều cách hiểu trong trường hợp không có ngữ cảnh, nó sẽ ghi lại một nút mơ hồ trong cây phân tích cú pháp với một số phân tích cú pháp có thể; kiểm tra kiểu quyết định cái nào là đúng và loại bỏ các cây con không hợp lệ). Bạn có thể thấy một cây phân tích (một phần) trong ví dụ dưới đây; toàn bộ cây quá lớn để phù hợp với câu trả lời SO. Lưu ý rằng bạn nhận được một cây phân tích cho dù giá trị 234797 hoặc 234799 được sử dụng.

Chạy trình phân giải tên / loại của công cụ trên AST với giá trị ban đầu 234799 thành công. Với giá trị 234797, trình phân giải tên không thành công (như mong đợi) với thông báo lỗi, "typen không phải là một loại." và do đó phiên bản đó không phải là một chương trình C ++ hợp lệ.

967 tree nodes in tree.
15 ambiguity nodes in tree.
(translation_unit@Cpp~GCC5=2#6b11a20^0 Line 1 Column 1 File C:/temp/prime_with_templates.cpp
 (declaration_seq@Cpp~GCC5=1021#6b06640^1#6b11a20:1 {10} Line 1 Column 1 File C:/temp/prime_with_templates.cpp
  (pp_declaration_seq@Cpp~GCC5=1022#6b049a0^1#6b06640:1 Line 1 Column 1 File C:/temp/prime_with_templates.cpp
   (declaration@Cpp~GCC5=1036#6b04980^1#6b049a0:1 Line 1 Column 1 File C:/temp/prime_with_templates.cpp
   |(template_declaration@Cpp~GCC5=2079#6b04960^1#6b04980:1 Line 1 Column 1 File C:/temp/prime_with_templates.cpp
   | (template_parameter_list@Cpp~GCC5=2082#6afbde0^1#6b04960:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |  (template_parameter@Cpp~GCC5=2085#6afbd80^1#6afbde0:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   (parameter_declaration@Cpp~GCC5=1611#6afbd40^1#6afbd80:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   |(basic_decl_specifier_seq@Cpp~GCC5=1070#6afb880^1#6afbd40:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   | (decl_specifier@Cpp~GCC5=1073#6afb840^1#6afb880:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   |  (trailing_type_specifier@Cpp~GCC5=1118#6afb7e0^1#6afb840:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   (simple_type_specifier@Cpp~GCC5=1138#6afb7a0^1#6afb7e0:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |  )trailing_type_specifier#6afb7e0
   |   | )decl_specifier#6afb840
   |   |)basic_decl_specifier_seq#6afb880
   |   |(ptr_declarator@Cpp~GCC5=1417#6afbc40^1#6afbd40:2 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   | (noptr_declarator@Cpp~GCC5=1421#6afbba0^1#6afbc40:1 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   |  (declarator_id@Cpp~GCC5=1487#6afbb80^1#6afbba0:1 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   (id_expression@Cpp~GCC5=317#6afbaa0^1#6afbb80:1 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |(unqualified_id@Cpp~GCC5=319#6afb9c0^1#6afbaa0:1 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   | (IDENTIFIER@Cpp~GCC5=3368#6afb780^1#6afb9c0:1[`V'] Line 1 Column 15 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |)unqualified_id#6afb9c0
   |   |   )id_expression#6afbaa0
   |   |  )declarator_id#6afbb80
   |   | )noptr_declarator#6afbba0
   |   |)ptr_declarator#6afbc40
   |   )parameter_declaration#6afbd40
   |  )template_parameter#6afbd80
   | )template_parameter_list#6afbde0
   | (declaration@Cpp~GCC5=1033#6b04940^1#6b04960:2 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |  (block_declaration@Cpp~GCC5=1050#6b04920^1#6b04940:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   (simple_declaration@Cpp~GCC5=1060#6b04900^1#6b04920:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |(basic_decl_specifier_seq@Cpp~GCC5=1070#6b048e0^1#6b04900:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   | (decl_specifier@Cpp~GCC5=1073#6b048c0^1#6b048e0:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |  (type_specifier@Cpp~GCC5=1110#6b048a0^1#6b048c0:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |   (class_specifier@Cpp~GCC5=1761#6b04880^1#6b048a0:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |   |(class_head@Cpp~GCC5=1763#6afb980^1#6b04880:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |   | (class_key@Cpp~GCC5=1791#6afbca0^1#6afb980:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp)class_key
   |   |   | (IDENTIFIER@Cpp~GCC5=3368#6afbcc0^1#6afb980:2[`answer'] Line 1 Column 25 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   | (optional_base_clause@Cpp~GCC5=1872#6afba60^1#6afb980:3 Line 1 Column 32 File C:/temp/prime_with_templates.cpp)optional_base_clause
   |   |   |)class_head#6afb980
   |   |   |(member_specification@Cpp~GCC5=1794#6b042e0^1#6b04880:2 {2} Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   | (member_declaration_or_access_specifier@Cpp~GCC5=1806#6b04060^1#6b042e0:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |  (member_declaration@Cpp~GCC5=1822#6b04040^1#6b04060:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   (function_definition@Cpp~GCC5=1632#6b04020^1#6b04040:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |(function_head@Cpp~GCC5=1673#6afbec0^1#6b04020:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   | (ptr_declarator@Cpp~GCC5=1417#6afbfe0^1#6afbec0:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |  (noptr_declarator@Cpp~GCC5=1422#6afbf80^1#6afbfe0:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   (noptr_declarator@Cpp~GCC5=1421#6afbf60^1#6afbf80:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(declarator_id@Cpp~GCC5=1487#6afbea0^1#6afbf60:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   | (id_expression@Cpp~GCC5=317#6afbb40^1#6afbea0:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |  (unqualified_id@Cpp~GCC5=319#6afbc80^1#6afbb40:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   (IDENTIFIER@Cpp~GCC5=3368#6afbc20^1#6afbc80:1[`answer'] Line 1 Column 34 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |   |   |  )unqualified_id#6afbc80
   |   |   |   |   | )id_expression#6afbb40
   |   |   |   |   |)declarator_id#6afbea0
   |   |   |   |   )noptr_declarator#6afbf60
   |   |   |   |   (parameter_declaration_clause@Cpp~GCC5=1559#6afbd00^1#6afbf80:2 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(pp_parameter_declaration_list@Cpp~GCC5=1570#6afb940^1#6afbd00:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   | (pp_parameter_declaration_seq@Cpp~GCC5=1574#6afb800^1#6afb940:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |  (parameter_declaration@Cpp~GCC5=1610#6afb9a0^1#6afb800:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   (basic_decl_specifier_seq@Cpp~GCC5=1070#6afbf40^1#6afb9a0:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |(decl_specifier@Cpp~GCC5=1073#6afbfa0^1#6afbf40:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   | (trailing_type_specifier@Cpp~GCC5=1118#6afbfc0^1#6afbfa0:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |  (simple_type_specifier@Cpp~GCC5=1140#6afb860^1#6afbfc0:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   |   |   |   | )trailing_type_specifier#6afbfc0
   |   |   |   |   |   |)decl_specifier#6afbfa0
   |   |   |   |   |   )basic_decl_specifier_seq#6afbf40
   |   |   |   |   |  )parameter_declaration#6afb9a0
   |   |   |   |   | )pp_parameter_declaration_seq#6afb800
   |   |   |   |   |)pp_parameter_declaration_list#6afb940
   |   |   |   |   )parameter_declaration_clause#6afbd00
   |   |   |   |   (function_qualifiers@Cpp~GCC5=1438#6afbce0^1#6afbf80:3 Line 1 Column 46 File C:/temp/prime_with_templates.cpp)function_qualifiers
   |   |   |   |  )noptr_declarator#6afbf80
   |   |   |   | )ptr_declarator#6afbfe0
   |   |   |   |)function_head#6afbec0
   |   |   |   |(function_body@Cpp~GCC5=1680#6b04000^1#6b04020:2 Line 1 Column 46 File C:/temp/prime_with_templates.cpp
   |   |   |   | (compound_statement@Cpp~GCC5=888#6afbee0^1#6b04000:1 Line 1 Column 46 File C:/temp/prime_with_templates.cpp)compound_statement
   |   |   |   |)function_body#6b04000
   |   |   |   )function_definition#6b04020
   |   |   |  )member_declaration#6b04040
   |   |   | )member_declaration_or_access_specifier#6b04060
   |   |   | (member_declaration_or_access_specifier@Cpp~GCC5=1806#6b042c0^1#6b042e0:2 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |  (member_declaration@Cpp~GCC5=1822#6b04820^1#6b042c0:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   (function_definition@Cpp~GCC5=1632#6b04280^1#6b04820:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   |(function_head@Cpp~GCC5=1674#6b04220^1#6b04280:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   | (basic_decl_specifier_seq@Cpp~GCC5=1070#6b040e0^1#6b04220:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   |  (decl_specifier@Cpp~GCC5=1073#6b040c0^1#6b040e0:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   |   (trailing_type_specifier@Cpp~GCC5=1118#6b040a0^1#6b040c0:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(simple_type_specifier@Cpp~GCC5=1138#6b04080^1#6b040a0:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   |   |   )trailing_type_specifier#6b040a0
   |   |   |   |  )decl_specifier#6b040c0
   |   |   |   | )basic_decl_specifier_seq#6b040e0
   |   |   |   | (ptr_declarator@Cpp~GCC5=1417#6b04200^1#6b04220:2 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |  (noptr_declarator@Cpp~GCC5=1422#6b041e0^1#6b04200:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   (noptr_declarator@Cpp~GCC5=1421#6b041a0^1#6b041e0:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(declarator_id@Cpp~GCC5=1487#6b04180^1#6b041a0:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   | (id_expression@Cpp~GCC5=317#6b04160^1#6b04180:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |  (unqualified_id@Cpp~GCC5=320#6b04140^1#6b04160:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   (operator_function_id@Cpp~GCC5=2027#6b04120^1#6b04140:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |(operator@Cpp~GCC5=2070#6b04100^1#6b04120:1 Line 1 Column 62 File C:/temp/prime_with_templates.cpp)operator
   |   |   |   |   |   )operator_function_id#6b04120
   |   |   |   |   |  )unqualified_id#6b04140
   |   |   |   |   | )id_expression#6b04160
   |   |   |   |   |)declarator_id#6b04180
   |   |   |   |   )noptr_declarator#6b041a0
   |   |   |   |   (parameter_declaration_clause@Cpp~GCC5=1558#6afba40^1#6b041e0:2 Line 1 Column 65 File C:/temp/prime_with_templates.cpp)parameter_declaration_clause
   |   |   |   |   (function_qualifiers@Cpp~GCC5=1438#6b041c0^1#6b041e0:3 Line 1 Column 66 File C:/temp/prime_with_templates.cpp)function_qualifiers
   |   |   |   |  )noptr_declarator#6b041e0
   |   |   |   | )ptr_declarator#6b04200
   |   |   |   |)function_head#6b04220
   |   |   |   |(function_body@Cpp~GCC5=1680#6b04300^1#6b04280:2 Line 1 Column 66 File C:/temp/prime_with_templates.cpp
   |   |   |   | (compound_statement@Cpp~GCC5=889#6b04760^1#6b04300:1 Line 1 Column 66 File C:/temp/prime_with_templates.cpp
   |   |   |   |  (pp_statement_seq@Cpp~GCC5=894#6b04780^1#6b04760:1 Line 1 Column 67 File C:/temp/prime_with_templates.cpp
   |   |   |   |   (statement@Cpp~GCC5=857#6b04440^1#6b04780:1 Line 1 Column 67 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(jump_statement@Cpp~GCC5=1011#6afba80^1#6b04440:1 Line 1 Column 67 File C:/temp/prime_with_templates.cpp
   |   |   |   |   | (pm_expression@Cpp~GCC5=551#6b04380^1#6afba80:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |  (cast_expression@Cpp~GCC5=543#6b04360^1#6b04380:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   (unary_expression@Cpp~GCC5=465#6b04340^1#6b04360:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |(primary_expression@Cpp~GCC5=307#6b04320^1#6b04340:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   | (id_expression@Cpp~GCC5=317#6b042a0^1#6b04320:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |  (unqualified_id@Cpp~GCC5=319#6b04260^1#6b042a0:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |   (IDENTIFIER@Cpp~GCC5=3368#6b04240^1#6b04260:1[`V'] Line 1 Column 74 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |   |   |   |  )unqualified_id#6b04260
   |   |   |   |   |   | )id_expression#6b042a0
   |   |   |   |   |   |)primary_expression#6b04320
   |   |   |   |   |   )unary_expression#6b04340
   |   |   |   |   |  )cast_expression#6b04360
   |   |   |   |   | )pm_expression#6b04380
   |   |   |   |   |)jump_statement#6afba80
   |   |   |   |   )statement#6b04440
   |   |   |   |  )pp_statement_seq#6b04780
   |   |   |   | )compound_statement#6b04760
   |   |   |   |)function_body#6b04300
   |   |   |   )function_definition#6b04280
   |   |   |  )member_declaration#6b04820
   |   |   | )member_declaration_or_access_specifier#6b042c0
   |   |   |)member_specification#6b042e0
   |   |   )class_specifier#6b04880
   |   |  )type_specifier#6b048a0
   |   | )decl_specifier#6b048c0
   |   |)basic_decl_specifier_seq#6b048e0
   |   )simple_declaration#6b04900
   |  )block_declaration#6b04920
   | )declaration#6b04940
   |)template_declaration#6b04960
   )declaration#6b04980
  )pp_declaration_seq#6b049a0
  (pp_declaration_seq@Cpp~GCC5=1022#6b06620^1#6b06640:2 Line 3 Column 1 File C:/temp/prime_with_templates.cpp
   (declaration@Cpp~GCC5=1036#6b06600^1#6b06620:1 Line 3 Column 1 File C:/temp/prime_with_templates.cpp
   |(template_declaration@Cpp~GCC5=2079#6b065e0^1#6b06600:1 Line 3 Column 1 File C:/temp/prime_with_templates.cpp
   | (template_parameter_list@Cpp~GCC5=2083#6b05460^1#6b065e0:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |  (template_parameter_list@Cpp~GCC5=2083#6b05140^1#6b05460:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   (template_parameter_list@Cpp~GCC5=2083#6b04ee0^1#6b05140:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |(template_parameter_list@Cpp~GCC5=2082#6b04cc0^1#6b04ee0:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   | (template_parameter@Cpp~GCC5=2085#6b04ca0^1#6b04cc0:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |  (parameter_declaration@Cpp~GCC5=1611#6b04c80^1#6b04ca0:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   (basic_decl_specifier_seq@Cpp~GCC5=1070#6b04a40^1#6b04c80:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   |(decl_specifier@Cpp~GCC5=1073#6b04a20^1#6b04a40:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   | (trailing_type_specifier@Cpp~GCC5=1118#6b04a00^1#6b04a20:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   |  (simple_type_specifier@Cpp~GCC5=1138#6b049e0^1#6b04a00:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   | )trailing_type_specifier#6b04a00
   |   |   |)decl_specifier#6b04a20
   |   |   )basic_decl_specifier_seq#6b04a40
   |   |   (ptr_declarator@Cpp~GCC5=1417#6b04c40^1#6b04c80:2 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |(noptr_declarator@Cpp~GCC5=1421#6b04be0^1#6b04c40:1 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   | (declarator_id@Cpp~GCC5=1487#6b04bc0^1#6b04be0:1 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |  (id_expression@Cpp~GCC5=317#6b04b60^1#6b04bc0:1 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |   (unqualified_id@Cpp~GCC5=319#6b04ac0^1#6b04b60:1 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |   |(IDENTIFIER@Cpp~GCC5=3368#6b049c0^1#6b04ac0:1[`no'] Line 3 Column 15 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |   )unqualified_id#6b04ac0
   |   |   |  )id_expression#6b04b60
   |   |   | )declarator_id#6b04bc0
   |   |   |)noptr_declarator#6b04be0
   |   |   )ptr_declarator#6b04c40
   |   |  )parameter_declaration#6b04c80
   |   | )template_parameter#6b04ca0
   |   |)template_parameter_list#6b04cc0
   |   |(template_parameter@Cpp~GCC5=2085#6b04ec0^1#6b04ee0:2 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   | (parameter_declaration@Cpp~GCC5=1611#6b04ea0^1#6b04ec0:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   |  (basic_decl_specifier_seq@Cpp~GCC5=1070#6b04b40^1#6b04ea0:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   |   (decl_specifier@Cpp~GCC5=1073#6b04ba0^1#6b04b40:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   |   |(trailing_type_specifier@Cpp~GCC5=1118#6b04c60^1#6b04ba0:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   |   | (simple_type_specifier@Cpp~GCC5=1138#6b04580^1#6b04c60:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   |)trailing_type_specifier#6b04c60
   |   |   )decl_specifier#6b04ba0
   |   |  )basic_decl_specifier_seq#6b04b40
   |   |  (ptr_declarator@Cpp~GCC5=1417#6b04e60^1#6b04ea0:2 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   (noptr_declarator@Cpp~GCC5=1421#6b04e40^1#6b04e60:1 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   |(declarator_id@Cpp~GCC5=1487#6b04de0^1#6b04e40:1 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   | (id_expression@Cpp~GCC5=317#6b04d80^1#6b04de0:1 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   |  (unqualified_id@Cpp~GCC5=319#6b04ce0^1#6b04d80:1 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   |   (IDENTIFIER@Cpp~GCC5=3368#6b04560^1#6b04ce0:1[`yes'] Line 3 Column 24 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |  )unqualified_id#6b04ce0
   |   |   | )id_expression#6b04d80
   |   |   |)declarator_id#6b04de0
   |   |   )noptr_declarator#6b04e40
   |   |  )ptr_declarator#6b04e60
   |   | )parameter_declaration#6b04ea0
   |   |)template_parameter#6b04ec0
   |   )template_parameter_list#6b04ee0
   |   (template_parameter@Cpp~GCC5=2085#6b05120^1#6b05140:2 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   |(parameter_declaration@Cpp~GCC5=1611#6b05100^1#6b05120:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   | (basic_decl_specifier_seq@Cpp~GCC5=1070#6b04d20^1#6b05100:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   |  (decl_specifier@Cpp~GCC5=1073#6b04dc0^1#6b04d20:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   |   (trailing_type_specifier@Cpp~GCC5=1118#6b04e80^1#6b04dc0:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   |   |(simple_type_specifier@Cpp~GCC5=1140#6b046e0^1#6b04e80:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   )trailing_type_specifier#6b04e80
   |   |  )decl_specifier#6b04dc0
   |   | )basic_decl_specifier_seq#6b04d20
   |   | (ptr_declarator@Cpp~GCC5=1417#6b05080^1#6b05100:2 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |  (noptr_declarator@Cpp~GCC5=1421#6b05020^1#6b05080:1 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |   (declarator_id@Cpp~GCC5=1487#6b05000^1#6b05020:1 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |   |(id_expression@Cpp~GCC5=317#6b04fa0^1#6b05000:1 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |   | (unqualified_id@Cpp~GCC5=319#6b04f00^1#6b04fa0:1 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |   |  (IDENTIFIER@Cpp~GCC5=3368#6b046c0^1#6b04f00:1[`f'] Line 3 Column 33 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   | )unqualified_id#6b04f00
   |   |   |)id_expression#6b04fa0
   |   |   )declarator_id#6b05000
   |   |  )noptr_declarator#6b05020
   |   | )ptr_declarator#6b05080
   |   |)parameter_declaration#6b05100
   |   )template_parameter#6b05120
   |  )template_parameter_list#6b05140
   |  (template_parameter@Cpp~GCC5=2085#6b05440^1#6b05460:2 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   (parameter_declaration@Cpp~GCC5=1611#6b05420^1#6b05440:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   |(basic_decl_specifier_seq@Cpp~GCC5=1070#6b05160^1#6b05420:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   | (decl_specifier@Cpp~GCC5=1073#6b04fe0^1#6b05160:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   |  (trailing_type_specifier@Cpp~GCC5=1118#6b050e0^1#6b04fe0:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   |   (simple_type_specifier@Cpp~GCC5=1140#6b050c0^1#6b050e0:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |  )trailing_type_specifier#6b050e0
   |   | )decl_specifier#6b04fe0
   |   |)basic_decl_specifier_seq#6b05160
   |   |(ptr_declarator@Cpp~GCC5=1417#6b053e0^1#6b05420:2 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   | (noptr_declarator@Cpp~GCC5=1421#6b053c0^1#6b053e0:1 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   |  (declarator_id@Cpp~GCC5=1487#6b05360^1#6b053c0:1 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   |   (id_expression@Cpp~GCC5=317#6b05280^1#6b05360:1 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   |   |(unqualified_id@Cpp~GCC5=319#6b051a0^1#6b05280:1 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   |   | (IDENTIFIER@Cpp~GCC5=3368#6b046a0^1#6b051a0:1[`p'] Line 3 Column 40 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |)unqualified_id#6b051a0
   |   |   )id_expression#6b05280
   |   |  )declarator_id#6b05360
   |   | )noptr_declarator#6b053c0
   |   |)ptr_declarator#6b053e0
   |   )parameter_declaration#6b05420
   |  )template_parameter#6b05440
   | )template_parameter_list#6b05460

Xác định xem đó là một khai báo biến hay phép nhân không phải là một tính năng kiểm tra kiểu. Ngoài ra tôi đã phải xem lại câu trả lời của bạn về thứ tự quảng cáo đó ... một lần nữa.
Cún con

@Puppy: bạn có thể nói những gì bạn thích, nhưng đó là cách công cụ hoạt động. Xóa tên công cụ có lẽ sẽ khiến mọi người hỏi tên công cụ là gì.
Ira Baxter

Cho dù đó có phải là cách công cụ hoạt động không liên quan hay không, vì câu hỏi không yêu cầu hoạt động của công cụ. Ngoài ra, tôi nghĩ rằng chúng ta có thể chờ đợi điều đó thực sự xảy ra.
Cún con
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.