2017-09-07 14 views
17

Tôi đang viết bằng C++ và tôi muốn vượt qua một loại vô danh (chỉ được biết đến trong thời gian chạy) để một hàm ảo thuần túy:C++ qua loại chưa biết đến một hàm ảo

virtual void DoSomething(??? data); 

nơi DoSomething là một thực hiện của một hàm ảo thuần túy trong một lớp dẫn xuất.

Tôi dự định sẽ sử dụng các mẫu nhưng vì nó biến ra chức năng ảo và các mẫu không có tác dụng với nhau: Can a C++ class member function template be virtual?

Tôi muốn tránh sử dụng một lớp cơ sở cho tất cả các lớp tôi vượt qua để chức năng (giống như object trong C#).

Cảm ơn trước

+1

bạn có thể xin được cụ thể hơn? Làm cách nào để bạn sử dụng dữ liệu bên trong hàm? Yêu cầu của loại dữ liệu là gì? Ví dụ. chỉ có một vài lớp được biết đến, hoặc bạn có kế hoạch chấp nhận bất kỳ lớp nào có một phương thức cụ thể, vv? – bolov

+1

Câu hỏi của bạn cần được thu hẹp một chút. Bạn có biết phạm vi của các loại, bạn có muốn chúng được tự động suy luận không? Câu trả lời đơn giản nhất sẽ là "sử dụng void *", một câu trả lời tốt hơn có thể là câu trả lời của Story Teller. Tất cả phụ thuộc vào usecase của bạn. –

+0

Tôi tò mò, nhìn vào các câu trả lời, về nếu có bất kỳ khả năng để đạt được kết quả tương tự mà không cần phải làm bất kỳ đúc trên 'doSomething'. Một cái gì đó giống như đóng gói kiểu theo cách nào đó mà không cần phải tạo mẫu 'doSomething' và lấy kiểu đó bằng' decltype', tức là kiểu phương thức nhà máy ảo ... – perencia

Trả lời

18

Bạn cần type erasure. Một ví dụ về điều này là mục đích chung boost::any (và std::any trong C++ 17).

virtual void DoSomething(boost::any const& data); 

Và sau đó mỗi sub-class có thể cố gắng các an toànany_cast để có được các dữ liệu mà nó mong đợi.

void DoSomething(boost::any const& data) { 
    auto p = any_cast<std::string>(&data); 

    if(p) { 
    // do something with the string pointer we extracted 
    } 
} 

Tất nhiên, bạn có thể xóa bỏ loại trừu tượng của mình nếu phạm vi hành vi bạn tìm kiếm bị hạn chế hơn.

0

một cái gì đó như thế ?:

class Foo 
{ 
    virtual ~Foo() = 0; 
}; 

template <typename T> 
class Bar : public Foo 
{ 
    T object; 
} 

... 

virtual void DoSomething(Foo* data) 
{ 
    Bar<int>* temp = dynamic_cast<Bar<int>*>(data); 
    if (temp) 
     std::count<<temp->object; 
} 
+6

'Foo' cần ít nhất một hàm thành viên ảo (có thể là hàm hủy), nếu không' dynamic_cast' sẽ không hoạt động. – Angew

+0

'Tôi muốn tránh sử dụng một lớp cơ sở cho tất cả các lớp tôi chuyển tới hàm (giống như đối tượng trong C#). ' – perencia

+0

Điều này không yêu cầu một lớp cơ sở cho mỗi đối tượng được truyền vào hàm, chỉ cho trình bao bọc' Bar'. Mặc dù điều này về mặt kỹ thuật là 'được truyền trong đối tượng', nhưng nó không phải là ý nghĩa của OP. – AzCopey

1

Nếu bạn không muốn sử dụng tăng/C++ 17 nào, hãy xem xét thu được những tham số của chức năng 'doSometing' từ một lớp cơ sở, và làm diễn viên năng động để đối tượng lớp phù hợp. Trong trường hợp này, bạn có thể kiểm tra trong thời gian chạy mà bạn có một con trỏ hợp lệ.

class param{ 
public: 
    virtual ~param(){}; 
}; 

template <typename T> 
struct specificParam:param{ 
    specificParam(T p):param(p){} 
    T param; 
}; 


class Foo 
{ 
public: 
    virtual void doSomething(param* data) = 0; 
}; 

template <typename T> 
class Bar : public Foo 
{ 
public: 
    virtual void doSomething(param* data){ 
     specificParam<T> *p = dynamic_cast<specificParam<T> *>(data); 

     if (p != nullptr){ 
      std::cout<<"Bar got:" << p->param << "\n"; 
     } 
     else { 
      std::cout<<"Bar: parameter type error.\n"; 
     } 
    } 
}; 

int main(){ 
    Bar<char> obj1; 
    Bar<int> obj2; 
    Bar<float> obj3; 

    specificParam<char> t1('a'); 
    specificParam<int> t2(1); 
    specificParam<float> t3(2.2); 

    obj1.doSomething(&t1); //Bar got:a 
    obj2.doSomething(&t2); //Bar got:1 
    obj3.doSomething(&t3); //Bar got:2.2 

    // trying to access int object with float parameter 
    obj2.doSomething(&t3); //Bar: parameter type error. 
} 

Cách đơn giản nhất (nhưng không an toàn!) Sẽ được sử dụng con trỏ void * + cast tĩnh

class Foo 
{ 
public: 
    virtual void doSomething(void* data) = 0; 
}; 

template <typename T> 
class Bar:public Foo 
{ 
public: 
    virtual void doSomething(void* data){ 
     T* pData = static_cast<T*>(data); 
     std::cout<<"Bar1 got:" << *pData << "\n"; 
    } 
}; 

int main(){ 

    Bar<char> obj1; 
    Bar<int> obj2; 
    Bar<float> obj3; 

    char c = 'a'; 
    int i = 1; 
    float f = 2.2; 

    obj1.doSomething(&c); // Bar1 got:a 
    obj2.doSomething(&i); // Bar1 got:1 
    obj3.doSomething(&f); // Bar1 got:2.2 

    //obj2.doSomething(&c); // Very bad!!!  
} 
1

Type-tẩy xoá không phải là khả năng duy nhất.

Bạn có thể quan tâm để sử dụng mẫu người truy cập: lấy như là đối số một std :: biến và ghé thăm nó với một lambda chứa mã mẫu mà bạn muốn thực hiện:

virtual void doSomething(std::variant<int,float/*,...*/> data) 
    { 
    visit([=](auto v){/*...*/;},data); 
    } 
Các vấn đề liên quan