这是一个我感到困惑的小问题,我不知道如何描述它,所以请看下面的代码:
struct B {
B() {}
B(B&) {
std::cout << "not trivial\n";
}
};
int main() {
B b1;
B b2(b1);
std::cout << std::is_trivially_constructible<B, B&>::value << '\n';
return 0;
}
输出是:
not trivial
1
我正在使用VS11。
修改
我刚刚在http://en.cppreference.com/w/cpp/types/is_constructible测试了这个例子 一些输出不正确。
#include <iostream>
#include <type_traits>
class Foo {
int v1;
double v2;
public:
Foo(int n) : v1(n), v2() {}
Foo(int n, double f) : v1(n), v2(f) {}
};
int main() {
std::cout << "Foo is ...\n" << std::boolalpha
<< "\tTrivially-constructible from const Foo&? "
<< std::is_trivially_constructible<Foo, const Foo&>::value << '\n'
<< "\tTrivially-constructible from int? "
<< std::is_trivially_constructible<Foo, int>::value << '\n'
<< "\tConstructible from int? "
<< std::is_constructible<Foo, int>::value << '\n'
}
输出是:
Foo is ...
Trivially-constructible from const Foo&? true
Trivially-constructible from int? true//Trivially-constructible from int? false
Constructible from int? true
Press any key to continue . . .
答案 0 :(得分:4)
最终更新
在@SebastianRedl发表非常有见地的评论后,我意识到标准的意图是引用对象的整个构造,而不仅仅是构造函数内部的操作。这意味着Visual C ++确实存在一个错误。但是,我仍然认为标准的措辞不够明确,所以我将保留其余的答案,以供后代使用。
澄清一下:OP提到的行为实际上是一个错误,鉴于此,我在此更新下面所说的大部分内容都是错误的。
结束更新
这实际上不是编译器错误,而是标准的一个奇怪的怪癖,所以你的困惑是可以理解的。
根据C ++ 11标准,以下是is_trivially_constructible::value
为true
的条件。
§20.9
is_constructible<T,Args...>::value
为真,并且is_constructible
的变量定义(如下所述)已知不会调用任何非平凡的操作
因此,is_trivially_constructible
为真,只要给定的类型可以使用给定的参数构造,并且它不会调用任何非平凡的操作。您的示例仅包含一个此类构造函数,一个复制构造函数。事实上,通过“非平凡操作”(本质上是一个非平凡的操作符或构造函数)的定义,这确实适用于您的类型。所以返回true
是正确的。
然而,有一个非常奇怪的观点! C + 11标准说明了以下关于复制构造函数的内容:
§12.8.12(强调我的)
如果非用户提供且
,那么X类的复制/移动构造函数是微不足道的
- 类X没有虚函数(10.3),没有虚基类(10.1)和
- 选择复制/移动每个直接基类子对象的构造函数是微不足道的,
对于类类型(或其数组)的X的每个非静态数据成员,选择复制/移动该成员的构造函数是微不足道的;
否则复制/移动构造函数是非常重要的。
由于您确实提供了用户定义的复制构造函数,因此您的类不是简单的可复制构造。你给出的拷贝构造函数并不简单。尽管如此,非平凡的复制构造函数确实满足is_trivially_constructible
返回true
的必要条件,给出与您的复制构造函数匹配的参数。
在我看来,这似乎更像是标准中的“错误”。 is_trivially_constructible
返回给定某些参数是否可以简单地构造类型。这似乎并不能保证构造函数本身被认为是微不足道的!
<强>更新强>
在尝试设计测试以显示以下情况后,我确实在VC11中发现了一个错误。标准描述的逻辑意味着,如果将B
用作另一种类型的子对象(成员或基础),则应调用该类型的任何调用B
的复制构造函数的构造函数。 std::is_trivially_constructible
的非平凡。在VC11中情况并非如此。
示例代码
#include <iostream>
#include <type_traits>
struct B
{
B() {}
B(B&) {
std::cout << "not trivial\n";
}
};
struct A : B
{
A(B& B) : b(B){}
B b;
};
int main()
{
std::cout << std::is_trivially_constructible<B, B&>::value << '\n'; // Should print 1
std::cout << std::is_trivially_constructible<A, B&>::value << '\n'; // Should print 0
getchar();
return 0;
}
答案 1 :(得分:1)
来自http://en.cppreference.com/w/cpp/types/is_constructible:
“构造函数表达式不会调用任何不重要的操作”。当你写
时就是这种情况B(B&);
没有什么花哨的东西在那里,你只是传递参考。