2017-05-20 17 views
11

My std::variant có thể để trống (std::monostate), chứa int, std::string hoặc bool.Tại sao biến thể của tôi chuyển đổi chuỗi std :: thành bool?

Khi tôi muốn cho nó ăn bằng một chuỗi, được cho là var = "this is my string", nó được chuyển đổi thành bool và không thành chuỗi. Nếu tôi khai báo kiểu rõ ràng, nó hoạt động var = std::string("this is my string"). Tại sao vậy và có điều gì tôi có thể làm để tránh nó?

#include <string> 
#include <variant> 
#include <iostream> 

int main() 
{ 
    using var = std::variant<std::monostate, int, std::string, bool>; 

    var contains_nothing;  
    var contains_int = 5; 
    var contains_string = "hello"; 
    var contains_expl_string = std::string("explicit hello"); 
    var contains_bool = false; 

    auto visitor = [](auto&& arg){ 
      using T = std::decay_t<decltype(arg)>; 
      if constexpr (std::is_same<T, std::monostate>()) 
       std::cout<<"nothing\n"; 
      else if constexpr (std::is_same<T, int>()) 
       std::cout<<"int: "<<arg<<"\n"; 
      else if constexpr (std::is_same<T, std::string>()) 
       std::cout<<"string: "<<arg<<"\n"; 
      else if constexpr (std::is_same<T, bool>()) 
       std::cout<<"bool: "<<arg<<"\n"; 
      else 
       std::cout<<"Visitor is not exhaustive\n"; 
     }; 

    std::visit(visitor, contains_nothing);  // nothing 
    std::visit(visitor, contains_int);   // int: 5 
    std::visit(visitor, contains_string);  // bool: 1 
    std::visit(visitor, contains_expl_string); // string: explicit hello 
    std::visit(visitor, contains_bool);   // bool: 0  
} 

EDIT Vì người dùng của mã của tôi có thể không thực hiện một cách rõ ràng string s, tôi muốn hiểu được. Nếu không, nó sẽ là một nguồn lỗi. Tôi đã thực hiện chức năng trợ giúp mẫu để kiểm tra nếu một char* đã được thông qua và nếu như vậy, làm cho một chuỗi :: std. Hoạt động tốt. Trợ giúp để làm cho điều này đơn giản hơn được đánh giá cao!

EDIT 2 Bằng cách khai báo std::monostate như tham số default/loại, nó thậm chí còn hoạt động khi make_var được gọi mà không cần bất kỳ cuộc tranh cãi.

#include <string> 
#include <variant> 
#include <iostream> 

using var = std::variant<std::monostate, int, std::string, bool>; 

template<typename T = std::monostate> 
var make_var(T value = std::monostate()) 
{ 
    if constexpr (std::is_same<typename std::remove_const<typename std::decay<T>::type>::type, const char*>()) 
     return std::string(value); 
    return value; 
} 

int main() 
{ 
    auto contains_nothing = make_var();  
    auto contains_int = make_var(3); 
    auto contains_string = make_var("hello"); 
    auto contains_expl_string = make_var(std::string("excplicit hello")); 
    var contains_bool = make_var(false); 

    auto visitor = [](auto&& arg){ 
      using T = std::decay_t<decltype(arg)>; 
      if constexpr (std::is_same<T, std::monostate>()) 
       std::cout<<"nothing\n"; 
      else if constexpr (std::is_same<T, int>()) 
       std::cout<<"int: "<<arg<<"\n"; 
      else if constexpr (std::is_same<T, std::string>()) 
       std::cout<<"string: "<<arg<<"\n"; 
      else if constexpr (std::is_same<T, bool>()) 
       std::cout<<"bool: "<<arg<<"\n"; 
      else 
       std::cout<<"Visitor is not exhaustive\n"; 
     }; 

    std::visit(visitor, contains_nothing); 
    std::visit(visitor, contains_int); 
    std::visit(visitor, contains_string); 
    std::visit(visitor, contains_expl_string); 
    std::visit(visitor, contains_bool);  
} 
+2

Một câu hỏi để hỏi là, tại sao nên 'std :: chuỗi' được ưa thích để 'bool'? Cả hai yêu cầu phân rã mảng và chuyển đổi loại. – juanchopanza

+1

Xem câu trả lời đáng yêu của tôi cho http://stackoverflow.com/questions/44021989/implicit-cast-from-const-string-to-bool đặc biệt là cách giải quyết của tôi bằng cách sử dụng mẫu ;-). Nhưng Angew sử dụng một người dùng được xác định bằng chữ (bên dưới) thì dễ thương hơn. – Bathsheba

+0

câu trả lời ngắn: nó không, bởi vì đó không phải là một 'std :: string' bạn đã cho nó. –

Trả lời

27

Loại "hello"const char [6], phân hủy thành const char *. Chuyển đổi từ const char * sang bool là một chuyển đổi được tích hợp sẵn, trong khi chuyển đổi từ const char * thành std::string là chuyển đổi do người dùng xác định, có nghĩa là chuyển đổi được thực hiện trước đây.

Vì bạn đang sử dụng C++> = 14, bạn có thể sử dụng hậu tố đen s để biểu thị một std::string đen:

using namespace std::string_literals; 

var contains_string = "hello"s; 
+1

Tại sao nó không chọn kiểu 'int' thay vì' bool'? – skypjack

+15

@skypjack Bởi vì không có con trỏ tiềm ẩn để chuyển đổi 'int' trong C++. – Angew

+0

Nó có ý nghĩa thực sự. Cảm ơn bạn.:-) – skypjack

4

"hello" không phải là một std::string, đó là một const char * và nó có thể được ngầm chuyển đổi sang bool cũng như sử dụng để xây dựng một std::string.
Nói cách khác, điều này chỉ hoạt động tốt:

int main() { 
    bool b = "foo"; 
    (void)b; 
} 

Như đã đề cập trong các ý kiến ​​của @aschepler:

Việc chuyển đổi ngầm const char*-bool được ưa thích hơn việc chuyển đổi người dùng định nghĩa từ const char* đến std::string. int không phải là tùy chọn.

+0

Không UB ở đây. Việc chuyển đổi ngầm từ 'const char *' thành 'bool' được ưu tiên hơn chuyển đổi do người dùng định nghĩa từ' const char * 'thành' std :: string'. 'int' không phải là một tùy chọn. – aschepler

+0

@aschepler Cảm ơn bạn đã sửa. – skypjack

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