Điều đáng ngạc nhiên với tôi là có rất nhiều câu trả lời khác nhau, nhưng hầu như tất cả đều dựa vào ma thuật mẫu nặng. Các mẫu rất mạnh mẽ, nhưng đôi khi các macro đánh bại chúng một cách đồng nhất. Tính linh hoạt tối đa thường đạt được bằng cách kết hợp cả hai.
Tôi đã viết một macro FROM_CONST_OVERLOAD()
có thể được đặt trong hàm non-const để gọi hàm const.
Ví dụ sử dụng:
class MyClass
{
private:
std::vector<std::string> data = {"str", "x"};
public:
// Works for references
const std::string& GetRef(std::size_t index) const
{
return data[index];
}
std::string& GetRef(std::size_t index)
{
return FROM_CONST_OVERLOAD( GetRef(index) );
}
// Works for pointers
const std::string* GetPtr(std::size_t index) const
{
return &data[index];
}
std::string* GetPtr(std::size_t index)
{
return FROM_CONST_OVERLOAD( GetPtr(index) );
}
};
Thực hiện đơn giản và có thể tái sử dụng:
template <typename T>
T& WithoutConst(const T& ref)
{
return const_cast<T&>(ref);
}
template <typename T>
T* WithoutConst(const T* ptr)
{
return const_cast<T*>(ptr);
}
template <typename T>
const T* WithConst(T* ptr)
{
return ptr;
}
#define FROM_CONST_OVERLOAD(FunctionCall) \
WithoutConst(WithConst(this)->FunctionCall)
Giải trình:
Như được đăng trong nhiều câu trả lời, mẫu điển hình để tránh trùng lặp mã trong hàm không phải là thành viên là:
return const_cast<Result&>( static_cast<const MyClass*>(this)->Method(args) );
Rất nhiều mẫu nồi hơi này có thể tránh được bằng cách sử dụng suy luận kiểu. Đầu tiên, const_cast
có thể được gói gọn trong WithoutConst()
đó, loại tham số kiểu đối số của nó và loại bỏ bộ định dạng. Thứ hai, một cách tiếp cận tương tự có thể được sử dụng WithConst()
để xác định this
con trỏ đủ điều kiện , cho phép gọi phương thức nạp chồng.
Phần còn lại là một macro đơn giản tiền tố cuộc gọi với đủ điều kiện chính xác this->
và loại bỏ const khỏi kết quả. Do biểu thức được sử dụng trong macro hầu như luôn luôn là một hàm gọi đơn giản với các đối số được chuyển tiếp 1: 1, các nhược điểm của các macro như nhiều đánh giá không khởi động. Dấu chấm lửng và __VA_ARGS__
cũng có thể được sử dụng, nhưng không cần thiết vì dấu phẩy (như phân tách đối số) xảy ra trong ngoặc đơn.
Cách tiếp cận này có một số lợi ích:
- Cú pháp tối thiểu và tự nhiên - chỉ cần kết thúc cuộc gọi
FROM_CONST_OVERLOAD( )
- Không có chức năng thành viên bổ sung cần thiết
- Tương thích với C ++ 98
- Thực hiện đơn giản, không có siêu lập trình mẫu và không phụ thuộc
- Extensible: Quan hệ const khác có thể được thêm vào (như
const_iterator
, std::shared_ptr<const T>
, vv). Đối với điều này, chỉ đơn giản là quá tải WithoutConst()
cho các loại tương ứng.
Hạn chế: giải pháp này được tối ưu hóa cho các tình huống trong đó tình trạng quá tải không phải là hoạt động chính xác giống như quá tải const, để các đối số có thể được chuyển tiếp 1: 1. Nếu logic của bạn khác và bạn không gọi phiên bản const qua this->Method(args)
, bạn có thể xem xét các phương pháp khác.