2017-01-17 14 views
5

Tôi đang gặp sự cố với thư viện chuẩn C++. Ví dụ sau không biên dịch: (lưu ý điều này được cắt giảm để tạo ra một ví dụ tối thiểu do đó không có ý nghĩa nhiều như nó)Làm cách nào để chỉ định toán tử đã quá tải trong một không gian tên khác?

#include <algorithm> 
#include <string> 
#include <vector> 

namespace otherns { 

class Property { 
public: 
    const std::string &getName() const { return m_name; } 

private: 
    std::string m_name; 
}; 
} 

bool operator==(const otherns::Property &a, const otherns::Property &b) { 
    return a.getName() == b.getName(); 
} 

/* Merge, second takes priority */ 
std::vector<otherns::Property> 
merge_props(const std::vector<otherns::Property> &xs, 
      const std::vector<otherns::Property> &ys) { 
    std::vector<otherns::Property> ans = ys; 
    for (const auto &x : xs) { 
    if (std::find(ans.begin(), ans.end(), x) == ans.end()) { 
     ans.push_back(x); 
    } 
    } 
    return ans; 
} 

Lỗi này là "nhị phân '==': không tìm thấy toán tử nào một toán hạng bên trái của loại 'otherns :: Property' (hoặc không có chuyển đổi chấp nhận được) "xảy ra ở đâu đó trong việc thực hiện std::find. Điều này là với MSVC nhưng tôi cũng đã thử với clang và gcc, với một kết quả tương tự.

Các mã sau đây làm việc:

std::vector<otherns::Property> 
merge_props(const std::vector<otherns::Property> &xs, 
      const std::vector<otherns::Property> &ys) { 
    std::vector<otherns::Property> ans = ys; 
    for (const auto &x : xs) { 
    if (std::find_if(ans.begin(), ans.end(), [&x](const otherns::Property &y) { 
      return x == y; 
     }) == ans.end()) { 
     ans.push_back(x); 
    } 
    } 
    return ans; 
} 

Tôi cho rằng đây là một cái gì đó để làm với tra cứu ADL/Koenig nhưng tôi thực sự không hiểu tại sao operator== của tôi không được tìm thấy. giải pháp tốt nhất là gì nếu tôi muốn sử dụng hình thức đầu tiên, đơn giản hơn của hàm find?

Thực tế, otherns xuất phát từ tiêu đề cho thư viện của bên thứ 3 nên tôi không thể đặt toán tử của mình vào tiêu đề đó.

+4

Không tìm thấy vì tìm kiếm phụ thuộc đối số chỉ tìm trong [* "Các không gian tên trong cùng bao quanh trong các lớp được thêm vào tập *"] (http://en.cppreference.com/w/cpp/language/adl). Vì toán tử của bạn nằm trong không gian tên chung, nó không bao giờ được xem xét. – StoryTeller

+3

đó là lý do tại sao bạn phải luôn khai báo các toán tử trong cùng một không gian tên như lớp – bolov

+0

Thư viện mà bạn sử dụng cố ý không cho phép ADL hoặc các tác giả có tội về sự thiếu sót. – StoryTeller

Trả lời

1

Các quy định là khá phức tạp, và bản thân tôi không nắm bắt đầy đủ họ, nhưng chúng ta hãy xem liệu chúng ta có thể làm cho người đứng đầu hoặc đuôi số họ (Tôi nghĩ rằng chúng ta có thể):

namespace nx { 
struct X {}; 
} 

namespace ns {  
auto foo(nx::X x1, nx::X x2) { return x1 == x2; } 
// error: no match for 'operator==' (operand types are 'nx::X' and 'nx::X') 
} 

auto operator==(nx::X, nx::X) { return true; } 

auto global_foo() 
{ 
    return ns::foo(nx::X{}, nx::X{}); 
} 

Đây không phải là được tìm thấy vì một lý do đơn giản: operator== không được khai báo trước khi sử dụng. Không có gì để với ADL được nêu ra. Càng xa càng tốt. Chúng tôi hiểu điều đó. Hãy sửa lỗi:

namespace nx { 
struct X {}; 
} 

auto operator==(nx::X, nx::X) { return true; } 

namespace ns { 
auto foo(nx::X x1, nx::X x2) { return x1 == x2; } 
} 

auto global_foo() 
{ 
    return ns::foo(nx::X{}, nx::X{}); 
} 

Tính năng này có hoạt động không? Đúng vậy, nó biên dịch và gọi số operator== của chúng tôi. Đây có phải là giải pháp đúng không? Không!. Bởi vì nếu chúng tôi thêm điều này:

namespace nx { 
struct X {}; 
} 

auto operator==(nx::X, nx::X) { return true; } // (1) 

namespace ns { 

template <class T> auto operator==(T, int) { return false; } // (2) 

auto foo(nx::X x1, nx::X x2) { return x1 == x2; } 
// error: no match for 'operator==' (operand types are 'nx::X' and 'nx::X') 
} 

auto global_foo() 
{ 
    return ns::foo(nx::X{}, nx::X{}); 
} 

Sau đó (2) trong ns ẩn (1) trong không gian tên chung, ngay cả khi (1) phù hợp hơn. Điều này được gọi là tên ẩn và - một lần nữa - không liên quan đến ADL theo bất kỳ cách nào.

Thậm chí tệ hơn:

namespace nx { 
struct X {}; 
} 

auto operator==(nx::X, nx::X) { return true; } // (1) 

namespace ns { 

template <class T> auto operator==(T, T) { return false; } // (2) 

auto foo(nx::X x1, nx::X x2) { return x1 == x2; } // calls (2) 
} 

auto global_foo() 
{ 
    return ns::foo(nx::X{}, nx::X{}); 
} 

sẽ biên dịch và âm thầm gọi (2) thay vì điều hành của chúng tôi (1).

Để biết ngữ cảnh thế giới thực, hãy nghĩ về namespace ns làm không gian tên std và bất kỳ toán tử nào được khai báo bên trong std. Và bạn đã có tình huống trong bài viết của bạn.

Các giải pháp đúng là:

namespace nx { 
struct X {}; 
auto operator==(nx::X, nx::X) { return true; } // (1) 
} 

namespace ns { 

template <class T> auto operator==(T, T) { return false; } // (2) 

auto foo(nx::X x1, nx::X x2) { return x1 == x2; } // calls (1) 
} 

auto global_foo() 
{ 
    return ns::foo(nx::X{}, nx::X{}); 
} 

gì xảy ra ở đây là đá ADL trong và mang (1) từ nx và bây giờ (1) được coi là bên cạnh (2). Nhưng (1) là chuyên biệt hơn (2) và do đó (1) được chọn chính xác.

Nếu bạn không có quyền kiểm soát namespace nx và không thể thêm toán tử tại đó, thì những gì tôi có thể khuyên là sử dụng các cuộc gọi thay vì dựa vào các toán tử. Ví dụ: thay vì std::find hãy sử dụng std::find_if với biến vị ngữ của riêng bạn (lambda) nơi bạn kiểm soát chính xác phương thức/toán tử nào cần gọi. Và khi tôi nói "chính xác", tôi có nghĩa là chính xác: tức là ::operator==(x1, x2) (hoặc bất kỳ không gian tên nào bạn đã khai báo) thay vì x1 == x2.


Bạn có thể đọc thêm về bài viết tuyệt vời này bởi Herb Sutter Namespaces & Interface Principle

1

Chỉ cần tuyên bố operator== bên trong namespace otherns (Các tra cứu sẽ tìm thấy nó ở phạm vi không gian tên)

namespace otherns { 
bool operator==(const otherns::Property &a, const otherns::Property &b) { 
    return a.getName() == b.getName(); 
} 
} 

Working code

Bạn có thể làm điều đó trong một tiêu đề riêng biệt của thư viện của bên thứ ba.

0

Bạn đã xác định operator== trong không gian tên toàn cầu (có thể bị đánh lừa bởi sự nhầm lẫn). Nó sẽ không được tìm thấy ở đó bằng cách tra cứu phụ thuộc vào đối số.

Các nhà điều hành cần được khai báo trong không gian tên giống như (một trong) đối số của nó:

namespace otherns { 

    class Property { 
    public: 
     const std::string &getName() const { return m_name; } 

    private: 
     std::string m_name; 
    }; 

    bool operator==(const otherns::Property &a, const otherns::Property &b) { 
     return a.getName() == b.getName(); 
    } 
} 

Đó chút thay đổi cho phép ví dụ của bạn để biên dịch sạch.

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