c++ - 当 T 声明 T::T(T&) 时, std::is_trivially_constructible<T, T&>::value 的结果不正确

标签 c++ type-traits

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

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

最佳答案

最终更新

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

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

结束更新

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

根据C++11标准,is_trivially_constructible::value的条件如下成为true .

§20.9

is_constructible<T,Args...>::value is true and the variable definition for is_constructible, as defined below, is known to call no operation that is not trivial

所以,is_trivially_constructible只要给定类型可以使用给定参数构造并且不调用任何重要操作,则为 true。您的示例仅包含一个这样的构造函数,即复制构造函数。事实上,根据“非平凡操作”(本质上是非平凡运算符或构造函数)的定义,这确实适用于您的类型。所以回来true是正确的。

不过,有一点很奇怪! C+11 标准对复制构造函数有如下规定:

§12.8.12(强调我的)

A copy/move constructor for class X is trivial if it is not user-provided and if

  • class X has no virtual functions (10.3) and no virtual base classes (10.1), and
  • the constructor selected to copy/move each direct base class subobject is trivial, and
  • for each non-static data member of X that is of class type (or array thereof), the constructor selected to copy/move that member is trivial;

    otherwise the copy/move constructor is non-trivial.

由于您确实提供了用户定义的复制构造函数,因此您的类不能简单地复制构造。您给出的复制构造函数并不简单。尽管如此,不平凡的复制构造函数确实满足 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;

}

关于c++ - 当 T 声明 T::T(T&) 时, std::is_trivially_constructible<T, T&>::value 的结果不正确,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16232547/

相关文章:

c++ - 动态规划算法

C++ 17 : How to call a different constructor using if constexpr?

c++ - 如何检查 C++ 中的类型 `T` 是否为 `std::pair<?, bool>`?

C++ 类型特征的概念

c++ - 类型特征 `is_noexcept` 、 `add_noexcept` 和 `remove_noexcept` ?

对象创建时的 C++ 未声明标识符

c++ - 为什么std::min仅支持initializer_list?

c++ - 加载时移除 Qt Webkit 加载光标

c++ - C++20 std::common_reference 的目的是什么?

arrays - 确定模板参数是否是持有相同非限定类型的数组