Tại sao ADL không tìm thấy các mẫu hàm?


83

Phần nào của đặc tả C ++ hạn chế tra cứu đối số phụ thuộc vào việc tìm các mẫu hàm trong tập hợp các không gian tên được liên kết? Nói cách khác, tại sao lệnh gọi cuối cùng mainbên dưới không biên dịch được?

namespace ns {
    struct foo {};
    template<int i> void frob(foo const&) {}
    void non_template(foo const&) {}
}

int main() {
    ns::foo f;
    non_template(f); // This is fine.
    frob<0>(f); // This is not.
}

Có nghĩa là bạn muốn làm việc frob () mà không viết ns :: frob ()?
Simon

Có, theo cách của một hàm không phải mẫu.
Hugh

FYI mã trên cũng không thành công trong Comeau: comeaucomputing.com/tryitout - thêm using namespace ns;hoặc ns::chứng chỉ vượt qua quá trình biên dịch. Đây là một câu hỏi hay.
fbrereto

3
@Huw: chỉ cần cắn nó :) Vui như thế nào rõ ràng trình độ cai trị ADL ra tôi đoán: /
Matthieu M.

1
@Matt: Haha, và tôi cũng vậy. Thế giới lập trình nhỏ.
GManNickG

Câu trả lời:


86

Phần này giải thích nó:

Tiêu chuẩn C ++ 03 14.8.1.6 :

[Lưu ý: Đối với tên hàm đơn giản, tra cứu phụ thuộc đối số (3.4.2) được áp dụng ngay cả khi tên hàm không hiển thị trong phạm vi của lệnh gọi. Điều này là do lệnh gọi vẫn có dạng cú pháp của một lệnh gọi hàm (3.4.1). Nhưng khi một mẫu hàm có các đối số mẫu rõ ràng được sử dụng, thì lệnh gọi không có dạng cú pháp chính xác trừ khi có mẫu hàm có tên đó hiển thị tại điểm gọi. Nếu không có tên nào như vậy được hiển thị, lệnh gọi không được định dạng tốt về mặt cú pháp và tra cứu phụ thuộc vào đối số không áp dụng. Nếu một số tên như vậy hiển thị, thì sẽ áp dụng tra cứu phụ thuộc đối số và có thể tìm thấy các mẫu hàm bổ sung trong các không gian tên khác.

namespace A {
  struct B { };
  template<int X> void f(B);
}
namespace C {
  template<class T> void f(T t);
}
void g(A::B b) {
  f<3>(b);    //ill-formed: not a function call
  A::f<3>(b); //well-formed
  C::f<3>(b); //ill-formed; argument dependent lookup
              // applies only to unqualified names
  using C::f;
  f<3>(b);    //well-formed because C::f is visible; then
              // A::f is found by argument dependent lookup
}

9
Cơ sở lý luận cho điều này là gì? Có vẻ như một yêu cầu kỳ lạ. Ý tôi là, hình thức cú pháp có liên quan gì?
Các cuộc đua ánh sáng trong quỹ đạo vào

22
@LightnessRacesinOrbit Phần 9.3.5 trong Vandevoorde & Josuttis giải thích tại sao đây vấn đề cú pháp (đặt tên được áp dụng cho ví dụ của OP): "trình biên dịch không thể quyết định đó f<3>(b)là đối số gọi hàm cho đến khi nó quyết định đó <3>là danh sách đối số mẫu. Ngược lại, chúng tôi không thể quyết định đó <3>là danh sách đối số mẫu cho đến khi chúng tôi tìm thấy đó f()là một mẫu. Vì không thể giải quyết vấn đề con gà và quả trứng này, nên biểu thức được phân tích cú pháp thành (f<3)>(b), điều này không có ý nghĩa gì. " Lưu ý rằng điều này tương tự như templatecú pháp phân định cho các mẫu hàm thành viên.
TemplateRex

9
Có đề xuất nào để khắc phục vấn đề này không? template f<3>(b)có thể là một cú pháp tốt hơn?
balki

1
Mặc dù vậy, biểu thức biểu thức @AngelusMortis ( ... ) (lưu ý thiếu toán tử trước ngoặc) luôn là một lời gọi hàm, và trình biên dịch biết điều đó ngay khi nó nhìn thấy dấu ngoặc mở. Vấn đề ở đây là nó <có thể vừa đóng vai trò là toán tử vừa là đầu danh sách đối số mẫu và trình biên dịch phải thực hiện nhiều phân tích cú pháp bổ sung để tìm ra cái nào (và có thể có một số cách sắp xếp mã mà điều này không thể thực hiện được để làm rõ ràng). Có vẻ như các tác giả tiêu chuẩn được bầu chọn để biến điều đó thành bất hợp pháp, có lẽ để cứu các nhà phát triển trình biên dịch.
Miral

2
Yêu cầu này đã được nâng lên trong C ++ 20 và mã OP của bây giờ cũng như hình thành :)
Rakete1111

8

Kể từ c ++ 20, adl cũng hoạt động tốt với mẫu hàm rõ ràng. Đây là đề xuất: P0846R0: Mẫu ADL và Hàm không Hiển thị :

Thay vì yêu cầu người dùng sử dụng từ khóa mẫu, một bản sửa đổi đối với các quy tắc tra cứu đã được đề xuất để một tên mà một tra cứu thông thường không tạo ra kết quả hoặc không tìm thấy một hoặc nhiều hàm và theo sau là aa "<" sẽ được coi là nếu một tên mẫu chức năng đã được tìm thấy và sẽ khiến ADL được thực hiện.

Hiện tại, chỉ có GCC 9 triển khai tính năng này, vì vậy ví dụ của bạn có thể biên dịch.

live demo.


5

Tôi muốn lọc lại câu trả lời được chấp nhận một chút. Nó không rõ ràng trong câu hỏi OP, nhưng phần quan trọng của tiêu chuẩn (trích dẫn bởi Kornel) là điều này (tôi nhấn mạnh):

Nhưng khi một mẫu hàm có các đối số mẫu rõ ràng được sử dụng, thì lệnh gọi không có dạng cú pháp chính xác

vì vậy điều bị cấm là dựa vào ADL và sử dụng các đối số mẫu rõ ràng. Thật không may khi sử dụng các đối số mẫu không phải kiểu yêu cầu sử dụng các đối số rõ ràng (trừ khi chúng có giá trị mặc định).

Dưới đây là mã mẫu hiển thị điều này:

[trực tiếp]

#include <string>
#include <utility>

namespace C {
  struct B { };
  template<class T> void f(T t){}
}

void g(C::B b) {
  f(b);           // OK
  //f<C::B>(b);   // ill-formed: not a function call, but only 
                  //  because explicit template argument were used

  std::string s;
  move(s);                      // OK
  //move<std::string&>(s);      // Error, again because 
                                //  explicit template argument were used
  std::move<std::string&>(s);   // Ok
}

int main()
{
 C::B b;
 g(b);
}

0

Chỉnh sửa: Không, điều này không đúng. Xem câu trả lời của @ Kornel .


Tôi không hoàn toàn chắc chắn nhưng đã tham khảo "Ngôn ngữ lập trình C ++" của Stroustrup, tôi nghĩ rằng Phụ lục C phần 13.8.4 có thể là nguyên nhân.

froblà một khuôn mẫu, người ta có thể hình dung nó chuyên biệt hóa nó i=0tại một thời điểm sau khi bạn gọi nó. Điều này có nghĩa là việc triển khai sẽ được để lại với hai cách có thể để chọn cách frobgọi khi nó xuất hiện, nó có thể chọn nó tại điểm khởi tạo hoặc khi kết thúc xử lý đơn vị dịch.

Vì vậy, tôi nghĩ vấn đề là bạn có thể làm

namespace ns {
    struct foo {};
    template<int i> void frob(foo const&) {}
}

int main() {
    ns::foo f;
    frob<0>(f);
    return 0;
}

namespace ns {
    template<> void frob< 0 >(foo const&) { /* Do something different*/ }
}

1
Không, bỏ không gian tên và bạn vẫn gặp vấn đề, phải không? Sự đặc biệt hóa sau khi sử dụng là một vấn đề bình thường trong C ++, biểu mẫu chuyên biệt không được sử dụng nếu được khai báo sau.
Kornel Kisielewicz

@Kornel: À vâng, đó là một lỗi khác, một lỗi khác phù hợp với những gì tôi đã mô tả. Đủ công bằng, cảm ơn vì đã chỉ ra điều đó.
Troubadour
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.