2015-09-26 26 views
10

Tôi đã có một kiểu liệt kê được định nghĩa bên trong một lớp học, và tôi muốn tạo một unordered_set của những đối tượng như một thành viên của lớp:Cách ghi đè std :: hash cho một enum được định nghĩa bên trong một lớp?

#include <unordered_set> 

class Foo { 
public: 
    enum Bar { 
    SOME_VALUE 
    }; 

    // Error: implicit instantiation of std::hash 
    std::unordered_set<Bar> getValues() const { 
    return _values; 
    } 

private: 
    std::unordered_set<Bar> _values; 
}; 

Bây giờ, tôi biết câu trả lời rõ ràng là thêm một băm chức năng tùy chỉnh để các unordered_set:

std::unordered_set<Bar, BarHasher> 

Tuy nhiên, những gì tôi đang tự hỏi là nếu có một cách chuyên std :: băm cho enum Bar để bất cứ ai sử dụng unordered_map được hành vi băm tự động.

Điều này làm việc với mọi loại dữ liệu khác, nhưng không hoạt động với tất cả các loại dữ liệu khác - vì không thể chuyển tiếp thư xác nhận. Để làm việc này, tôi phải đặt định nghĩa của std :: hash sau định nghĩa enum, nhưng trước khi sử dụng lần đầu tiên, có nghĩa là tôi phải đặt nó ở giữa lớp cơ thể, mà sẽ không hoạt động.

+0

Enums có thể được chuyển tiếp tuyên bố, nhưng tôi không thấy nó giúp như thế nào. – chris

+1

Điều này đã được sửa trong c + + 14: http://stackoverflow.com/a/29618545/951890 –

Trả lời

3

Tuy nhiên, những gì tôi đang tự hỏi là nếu có một cách chuyên std :: băm cho enum Bar để bất cứ ai sử dụng unordered_map được hành vi băm tự động.

Không có phép lạ, vì vậy, mọi người sẽ chỉ sử dụng chuyên ngành std::hash chỉ sau khi chuyên môn hóa. Vì bạn không thể chuyên các lớp bên trong lớp khác và enum của bạn được lồng nhau nên sẽ có vấn đề khi sử dụng std::hash bên trong lớp. Như bạn đã chỉ ra enums không thể được chuyển tiếp tuyên bố. Vì vậy, có giải pháp duy nhất (không tạo các lớp cơ sở hoặc "không cần thiết" enums) để sử dụng chuyên ngành std::hash bên trong lớp: tổng hợp/khai báo theo tham chiếu và sử dụng bên ngoài sau khi chuyên môn std::hash.

#include <iostream> 
#include <unordered_set> 
#include <memory> 

struct A { 

    enum E { 
     first, second 
    }; 

    A(); 

    std::unique_ptr< std::unordered_set<E> > s_; //!< Here is 
}; 

namespace std { 

template<> 
class hash<A::E> { 
public: 
    std::size_t operator()(A::E const& key) const noexcept { 
     std::cout << "hash<A::E>::operator()" << std::endl; 
     return key; 
    } 

}; 

} 

A::A() 
    : s_(new std::unordered_set<E>) 
{ } 

int main(void) { 
    A a; 
    a.s_->insert(A::first); 

    std::unordered_set<A::E> s; 
    s.insert(A::second); 
} 

In ra

băm < A :: E> :: operator()
băm < A :: E> :: operator()

Vì vậy, bên ngoài lớp A mọi người đều có thể sử dụng A::E với số std::hash cũng như trong lớp chúng tôi cũng sử dụng A::E với std::hash. Ngoài ra, nếu bạn không muốn tổng hợp std::unordered_set bằng cách tham chiếu, bạn có thể triển khai chỉ cho tùy chỉnh sử dụng nội bộ (và sau đó chuyển tiếp std::hash cuộc gọi đến nó).

1

Dường như bạn đã bao phủ tất cả các góc đã có trong câu hỏi của mình.

Tôi không thể nghĩ ra cách để thực hiện việc này.

Để tóm tắt lại, bạn chỉ có thể thay đổi các sự kiện của tình hình:

  • làm cho enum phi lồng nhau (đặt nó trong một không gian tên kèm theo thay vì), hoặc
  • sử dụng chức năng hasher một cách rõ ràng như trong ví dụ của bạn.
3

Một khả năng là đặt enum vào lớp cơ sở. Thật không may, bạn phải cung cấp một tuyên bố sử dụng cho mỗi thành viên enum. Một cách xung quanh đó là sử dụng một enum scoped (enum class Bar), mà đòi hỏi phải sử dụng như Foo::Bar::SOME_VALUE thay vì Foo::SOME_VALUE. Làm như vậy, bạn chỉ cần using FooBase::Bar;.

class FooBase { 
public: 
    enum Bar { 
    SOME_VALUE 
    }; 

protected: 
    ~FooBase() = default; //so can't be used polymorphically 
}; 

//hash goes here 

class Foo : FooBase { 
public: 
    using FooBase::Bar; 
    using FooBase::SOME_VALUE; 
    ... 
+0

Ooh đó là sáng tạo. Tôi sẽ không sử dụng nó, nhưng tôi đã cười khúc khích. :) –

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