boost::variant - 为啥“const char*”转换为“bool”?

Posted

技术标签:

【中文标题】boost::variant - 为啥“const char*”转换为“bool”?【英文标题】:boost::variant - why is "const char*" converted to "bool"?boost::variant - 为什么“const char*”转换为“bool”? 【发布时间】:2012-11-07 11:15:37 【问题描述】:

我声明了一个boost::variant,它接受三种类型:stringboolint。以下代码显示我的变体接受const char* 并将其转换为boolboost::variant 接受和转换不在其列表中的类型是正常行为吗?

#include <iostream>
#include "boost/variant/variant.hpp"
#include "boost/variant/apply_visitor.hpp"

using namespace std;
using namespace boost;

typedef variant<string, bool, int> MyVariant;

class TestVariant
    : public boost::static_visitor<>

public:
    void operator()(string &v) const
    
        cout << "type: string -> " << v << endl;
    
    template<typename U>
    void operator()(U &v)const
    
        cout << "type: other -> " << v << endl;
    
;

int main(int argc, char **argv) 

    MyVariant s1 = "some string";
    apply_visitor(TestVariant(), s1);

    MyVariant s2 = string("some string");
    apply_visitor(TestVariant(), s2);

    return 0;

输出:

类型:其他 -> 1 类型:字符串 -> 一些字符串

如果我从 MyVariant 中删除 bool 类型并将其更改为:

typedef variant<string, int> MyVariant;

const char* 不再转换为 bool。这次它被转换为string,这是新的输出:

类型:字符串 -> 一些字符串 类型:字符串 -> 一些字符串

这表示variant 尝试先将其他类型转换为bool,然后再转换为string。如果类型转换是不可避免的并且应该总是发生,有没有办法给string的转换提供更高的优先级?

【问题讨论】:

【参考方案1】:

这与boost::variant 无关,而是与 C++ 选择要应用的转换的顺序有关。在尝试使用用户定义的转换之前(请记住,std::string 是用于此目的的用户定义的类),编译器将尝试内置转换。没有从const char*int 的内置转换,但根据标准中的§4.12:

[...] 指针 [...] 类型的纯右值可以转换为 bool 类型的纯右值。

因此编译器很乐意将您的const char* 转换为bool,并且永远不会考虑将其转换为std::string

更新:看起来这个明显不需要的转换正在修复。你可以找到修复here的技术解释。

【讨论】:

今天这对我来说是一对重载的方法,一个是bool,另一个是const std::string&amp;。不幸!【参考方案2】:

我认为这与boost::variant 没有什么特别的关系,它与重载决议选择了哪个构造函数有关。重载函数也会发生同样的事情:

#include <iostream>
#include <string>

void foo(bool) 
    std::cout << "bool\n";


void foo(std::string) 
    std::cout << "string\n";


int main() 
    foo("hi");

输出:

bool

我不知道如何更改 Variant 具有的构造函数 [编辑:正如 James 所说,您可以编写另一个在其实现中使用 Variant 的类。然后你可以提供一个 const char* 构造函数来做正确的事情。]

也许您可以更改 Variant 中的类型。另一个重载示例:

struct MyBool 
    bool val;
    explicit MyBool(bool val) : val(val) 
;

void bar(MyBool) 
    std::cout << "bool\n";


void bar(const std::string &) 
    std::cout << "string\n";


int main() 
    bar("hi");

输出:

string

不幸的是,现在您必须写 bar(MyBool(true)) 而不是 foo(true)。对于string/bool/int 的变体,更糟糕的是,如果您只是将其更改为string/MyBool/int 的变体,那么MyVariant(true) 将调用int 构造函数。

【讨论】:

为了完成你的解释:从任何指针类型到bool 都有一个隐式转换,并且总是优先选择内置隐式转换而不是用户定义的转换。 (char*std::string 的转换算用户自定义。) 至于改变构造函数,你可以将类包装在另一个类中,或者从它派生。根据上下文,其中之一可能合适,也可能不合适;两者都有一些缺点。 我认为一种解决方案是从 MyVariant 中删除 bool 并改用 0 和 1 值。 @Meysam:是的。我考虑过推荐它,但后来我想你可能希望用0 初始化的MyVariant 与用false 初始化的MyVariant 不同。如果它们可以相同,只需删除bool。如果它们有不同的含义,那就没那么简单了。【参考方案3】:

我猜你想使用的是字符串文字(不是 const char * 本身)。

然后,你可以试试 std::string_literals(std::string 的 s-suffix)。

#include <iostream>
#include "boost/variant/variant.hpp"
#include "boost/variant/apply_visitor.hpp"

#include <string>
using namespace std::string_literals; // enables s-suffix for std::string literals

using namespace std;
using namespace boost;

typedef variant<string, bool, int> MyVariant;

class TestVariant
    : public boost::static_visitor<>

public:
    void operator()(string &v) const
    
        cout << "type: string -> " << v << endl;
    
    template<typename U>
    void operator()(U &v)const
    
        cout << "type: other -> " << v << endl;
    
;

int main(int argc, char **argv) 

    MyVariant s1 = "some string"s; // use s-suffix ("some string"s is std::string, not const char *
    apply_visitor(TestVariant(), s1);

    MyVariant s2 = string("some string");
    apply_visitor(TestVariant(), s2);

    return 0;

输出:

type: string -> some string
type: string -> some string

【讨论】:

以上是关于boost::variant - 为啥“const char*”转换为“bool”?的主要内容,如果未能解决你的问题,请参考以下文章

boost::variant 隐式转换为字符串

boost::const 和 non-const 的变体

将由 boost::variant 聚合的类型的对象传递给接受该 boost::variant 的函数

如何返回由 boost::variant 返回类型中包含的类型的子集组成的 boost::variant

Boost::Variant 和其中的 function_types:如何将函数放入 Boost::variant?

boost::variant:递归向量类型的奇怪行为