Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/xamarin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 基于积分类型的两类间隐式转换_C++_Operator Overloading_Implicit Conversion - Fatal编程技术网

C++ 基于积分类型的两类间隐式转换

C++ 基于积分类型的两类间隐式转换,c++,operator-overloading,implicit-conversion,C++,Operator Overloading,Implicit Conversion,在这种情况下,我有一个类a,它为整数类型提供构造函数,还有一个类B,它为相同的整数类型提供隐式转换运算符。但是,如果我调用一个函数,该函数接受对类a的引用,并带有类B的实例,则编译失败。我希望类B隐式转换为类A的构造函数所接受的类型。当然,如果我在a接受类B中添加一个构造函数,一切都很好。这种行为是故意的吗?请检查下面的示例 #include <iostream> class B { public: B() = default; B(const st

在这种情况下,我有一个类
a
,它为整数类型提供构造函数,还有一个类
B
,它为相同的整数类型提供隐式转换运算符。但是,如果我调用一个函数,该函数接受对类
a
的引用,并带有类
B
的实例,则编译失败。我希望类
B
隐式转换为类
A
的构造函数所接受的类型。当然,如果我在
a
接受类
B
中添加一个构造函数,一切都很好。这种行为是故意的吗?请检查下面的示例

#include <iostream>

class B
{
public:
        B() = default;
        B(const std::uint8_t &val) : mVal(val) {}

        std::uint8_t get() const { return mVal; }

        operator std::uint8_t() const { return mVal; }

private:
        std::uint8_t mVal;
};

class A
{
public:
        A() = default;
        A(const std::uint8_t &val) : mVal(val) {}

        // No problem if this exists
        // A(const B &b) : mVal(b.get()) {}

        std::uint8_t get() const { return mVal; }

private:
        std::uint8_t mVal;
};

void func(const A &a)
{
        std::cout << static_cast<int>(a.get()) << std::endl;
}

int main(int, char*[])
{
        std::uint8_t val = 0xCE;

        A a(val);
        B b(val);

        func(val); // fine
        func(a); // fine
        func(b); // error
}
#包括
B类
{
公众:
B()=默认值;
B(const std::uint8_t&val):mVal(val){}
std::uint8_t get()常量{return mVal;}
运算符std::uint8_t()常量{return mVal;}
私人:
标准:uint8_t mVal;
};
甲级
{
公众:
A()=默认值;
A(const std::uint8_t&val):mVal(val){}
//如果存在,没有问题
//A(constb&B):mVal(B.get()){}
std::uint8_t get()常量{return mVal;}
私人:
标准:uint8_t mVal;
};
无效函数(常数A&A)
{
标准::cout
这种行为是故意的吗

是的,这是有意的

隐式转换序列最多可以有一个用户定义的转换(构造函数或转换函数)

标准说(强调我的):

[over.ics.user]

用户定义的转换序列由初始标准转换序列和A用户组成- 定义的转换(15.3),然后是第二个标准转换序列


为了使用户定义的类型(类)可以隐式转换为另一个类型,必须有直接转换为该类型的构造函数或转换运算符。不能通过中间类型进行隐式转换(从用户定义的类型转换为另一个类型)


您可以使用显式转换。

在隐式创建对象时,您只允许进行一次用户定义的转换。由于
func
需要
a
,您可以进行一次用户定义的转换,将
B
转换为
std::uint8\t
,然后进行另一次用户定义的转换,将
std::ui>转换为
nt8\u t
转换为
A
。您需要的是
B
中的
操作符A
,或者
A
中的构造函数,如果您希望隐式发生,它将接受
B
。否则,您可以显式强制转换,这样您只需要一个隐式构造函数,如

func(static_cast<std::uint8_t>(b)); // force it to a uint8_t
// or
func({b}); // make b the direct initializer for an A which will implicitly cast
// or
func(A{b}); same as #2 above but explicitly sating it
func(static_cast(b));//强制将其转换为uint8\t
//或
func({b});//使b成为将隐式强制转换的A的直接初始值设定项
//或
func(A{b});与上面的#2相同,但显式满足它

在C++中有一个规则,即没有隐式转换将使用两个用户定义的转换。 这是因为这种“远距离”转换可能会产生极其令人惊讶的结果

如果您希望能够从任何可以转换为
uint8\t
的内容转换,您可以执行以下操作:

template<class IntLike,
  std::enable_if_t<std::is_convertible_v<IntLike, std::uint8_t>, bool> =true,
  std::enable_if_t<!std::is_same_v<A, std::decay_t<IntLike>>, bool> =true
>
A( IntLike&& intlike ):A( static_cast<std::uint8_t>(std::forward<IntLike>(intlike)) )
{}
存在以确保仅当转换的类型具有所需的属性时才使用重载

第一个
enable\u if
子句声明我们只需要可以转换为
uint8\t
的东西。第二个声明我们不希望此构造函数用于类型
A
本身,即使它通过了第一个

无论何时为类型创建转发引用隐式构造函数,都非常需要第二个子句,否则会遇到一些其他令人惊讶的问题


所使用的技术称为SFINAE,或者替换失败不是错误。当推导出类型
IntType
,并且这些测试失败时,这些子句中存在替换失败。通常这会导致错误,但当评估模板重载时,这不是错误,因为SFINAE;相反,它只是阻止该模板从b在重载解析中考虑。

只允许一个用户转换,这里需要2个。
B
->
std::uint8\t
->
A
@Yksisarvinen严格来说,您的注释是错误的。您可以使用任何可以转换为A
B
的内容。如果编译器需要A
B
,它并不总是有to be a
B
func({B})
甚至更短,但对于“另一方”(
func(a{B})
)。@Jarod42也包括这些
  std::enable_if_t<std::is_convertible_v<IntLike, std::uint8_t>, bool> =true,
  std::enable_if_t<!std::is_same_v<A, std::decay_t<IntLike>>, bool> =true