3

这是一个让我感到困惑的小问题。我不知道如何描述它,所以只看下面的代码:

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
4

2 回答 2

4

最后更新

在@SebastianRedl 发表了非常有见地的评论后,我意识到该标准的目的是指代对象的整个构造,而不仅仅是构造函数内部的操作。这意味着在 Visual C++ 中确实存在一个错误。但是,我仍然认为标准的措辞不够明确,因此我将保留其余答案以供后人使用。

澄清一下:OP提到的行为实际上是一个错误,鉴于此,我在此更新下面所说的大部分内容都是错误的。

结束更新

这实际上不是编译器错误,而是标准的一个奇怪的怪癖,所以你的困惑是可以理解的。

根据 C++11 标准,以下是is_trivially_constructible::valueto的条件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;

}

于 2013-04-26T09:55:36.073 回答
1

来自http://en.cppreference.com/w/cpp/types/is_constructible

“构造函数表达式不调用任何不平凡的操作”。当你写的时候就是这种情况

B(B&);

没什么特别的,你只需传入一个参考。

于 2013-04-26T09:08:28.190 回答