为什么 C++11 中的统一初始化与虚基数 类 的行为很奇怪?
Why does uniform initialization in C++11 behave weirdly with virtual base classes?
现在,我正在学习 C++ 中的继承特性,想测试一下最近学习的虚拟基础概念 classes。
我尝试了以下简单代码:
#include <iostream>
using namespace std;
class A
{
private:
int m_value;
string m_caller;
public:
A(int p_value, string p_caller) : m_value{p_value}, m_caller{p_caller}
{
cout<<"Instantiating A via "<<m_caller<<endl;
}
};
class B : virtual public A
{
private:
int m_value;
public:
B(int p_value1,int p_value2) : A{p_value1,"B"}, m_value{p_value2}
{
cout<<"Instantiating B."<<endl;
}
};
class C : public B
{
public:
C(int p_value1,int p_value2) : A{p_value1,"C"}, B(p_value1, p_value2)
{
cout<<"Instantiating C."<<endl;
}
};
int main()
{
C c1(1,2);
return 0;
}
请注意 class C 的构造函数中的 B(p_value1, p_value2)
。这给了我想要的输出:
Instantiating A via C
Instantiating B.
Instantiating C.
但是,当我将其更改为 B{p_value1, p_value2}
时,我得到了以下输出:
Instantiating A via C
Instantiating A via B
Instantiating B.
Instantiating C.
我试图寻找答案,但我得到的所有答案都引用了一些 C++ 标准。作为 OOP 的初学者,我正在为这种行为寻找更简单的解释。
非常感谢!
P.S。我在 Windows 中使用 C::B 和编译器 g++ 4.8.1.
这是 g++ 中的编译器错误。
在 C++14 (N4140) 部分 [dcl.init.list] 中,列表初始化的定义是(为简洁起见进行了编辑):
List-initialization of an object or reference of type T
is defined as follows:
- If
T
is an aggregate, aggregate initialization is performed
- Otherwise, if the initializer list has no elements and
T
is a class type with a default constructor, the object is value-initialized.
- Otherwise, if
T
is a specialization of std::initializer_list, [...]
- Otherwise, if
T
is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution. If a narrowing conversion is required to convert any of the arguments, the program is ill-formed.
- [...]
前 3 点不适用:B
不是聚合(聚合不能有基数 类),初始化列表确实有元素,B
不是std::initializer_list
.
专业化
第四点确实适用,因为根据 [over.match.list]/1.2:
,重载解析将 B{p_value1, p_value2}
匹配到构造函数 B(int, int)
If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class T
and the argument list consists of the elements of the initializer list.
根据最后引述,B(whatever)
和 B{whatever}
的行为应该相同。
现在,我正在学习 C++ 中的继承特性,想测试一下最近学习的虚拟基础概念 classes。 我尝试了以下简单代码:
#include <iostream>
using namespace std;
class A
{
private:
int m_value;
string m_caller;
public:
A(int p_value, string p_caller) : m_value{p_value}, m_caller{p_caller}
{
cout<<"Instantiating A via "<<m_caller<<endl;
}
};
class B : virtual public A
{
private:
int m_value;
public:
B(int p_value1,int p_value2) : A{p_value1,"B"}, m_value{p_value2}
{
cout<<"Instantiating B."<<endl;
}
};
class C : public B
{
public:
C(int p_value1,int p_value2) : A{p_value1,"C"}, B(p_value1, p_value2)
{
cout<<"Instantiating C."<<endl;
}
};
int main()
{
C c1(1,2);
return 0;
}
请注意 class C 的构造函数中的 B(p_value1, p_value2)
。这给了我想要的输出:
Instantiating A via C
Instantiating B.
Instantiating C.
但是,当我将其更改为 B{p_value1, p_value2}
时,我得到了以下输出:
Instantiating A via C
Instantiating A via B
Instantiating B.
Instantiating C.
我试图寻找答案,但我得到的所有答案都引用了一些 C++ 标准。作为 OOP 的初学者,我正在为这种行为寻找更简单的解释。 非常感谢!
P.S。我在 Windows 中使用 C::B 和编译器 g++ 4.8.1.
这是 g++ 中的编译器错误。
在 C++14 (N4140) 部分 [dcl.init.list] 中,列表初始化的定义是(为简洁起见进行了编辑):
List-initialization of an object or reference of type
T
is defined as follows:
- If
T
is an aggregate, aggregate initialization is performed- Otherwise, if the initializer list has no elements and
T
is a class type with a default constructor, the object is value-initialized.- Otherwise, if
T
is a specialization of std::initializer_list, [...]- Otherwise, if
T
is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution. If a narrowing conversion is required to convert any of the arguments, the program is ill-formed.- [...]
前 3 点不适用:B
不是聚合(聚合不能有基数 类),初始化列表确实有元素,B
不是std::initializer_list
.
第四点确实适用,因为根据 [over.match.list]/1.2:
,重载解析将B{p_value1, p_value2}
匹配到构造函数 B(int, int)
If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class
T
and the argument list consists of the elements of the initializer list.
根据最后引述,B(whatever)
和 B{whatever}
的行为应该相同。