当 T 声明为 T::T(T&amp;) 时 std::is_trivially_constructible<T, T&&gt;::value 结果不正确 [英] Incorrect result for std::is_trivially_constructible&lt;T, T&amp;&gt;::value when T declared T::T(T&amp;)

查看:32
本文介绍了当 T 声明为 T::T(T&amp;) 时 std::is_trivially_constructible<T, T&&gt;::value 结果不正确的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

struct B {B(){}B(B&){std::cout <<不是微不足道的\n";}};int main() {Bb1;B b2(b1);std::cout <<std::is_trivially_constructible<B, B&>::value <<'\n';返回0;}

输出为:

不是微不足道的1

我使用的是 VS11.

我刚刚测试了 http://en.cppreference.com/w 中的示例/cpp/types/is_constructible.部分输出不正确.

#include #include 类 Foo {int v1;双 v2;民众:Foo(int n) : v1(n), v2() {}Foo(int n, double f) : v1(n), v2(f) {}};int main() {std::cout <<Foo是......\n"<<标准::布尔阿尔法<<"\t从 const Foo&?"<<std::is_trivially_constructible<Foo, const Foo&>::value <<'\n'<<"\t可以从 int 简单地构造?"<<std::is_trivially_constructible<Foo, int>::value <<'\n'<<"\t可从 int 构造?"<<std::is_constructible<Foo, int>::value <<'\n'}

输出为:

Foo 是 ...可从 const Foo& 轻松构建?真的可以从 int 简单地构造?true//可以从 int 简单构造?错误的可以从 int 构造?真的

解决方案

FINAL UPDATE

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

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

结束更新

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

根据C++11标准,以下是is_trivially_constructible::valuetrue的条件.

§20.9

<块引用>

is_constructible 为真,并且 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<的复制构造函数的该类型的任何构造函数/code> 应被 std::is_trivially_constructible 视为非平凡.在 VC11 中不是这种情况.

示例代码

#include #include 结构体B{B(){}B(B&){std::cout <<"不是微不足道的\n";}};结构A:B{A(B& B) : b(B){}乙乙;};int main(){std::cout <<std::is_trivially_constructible<B, B&>::value <<'\n';//应该打印 1std::cout <<std::is_trivially_constructible<A, B&>::value <<'\n';//应该打印 0getchar();返回0;

}

This is a little problem that I get confused with. I don't know how to describe it, so just see the codes below:

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;
}

The output is:

not trivial
1

I'm using VS11.

EDIT:

I just tested the example in http://en.cppreference.com/w/cpp/types/is_constructible. Some of the output is incorrect.

#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'
}

The output is:

Foo is ...
        Trivially-constructible from const Foo&? true
        Trivially-constructible from int? true//Trivially-constructible from int? false
        Constructible from int? true

解决方案

FINAL UPDATE

After a very insightful comment by @SebastianRedl I realized that the intention of the standard is to refer to the entire construction of the object, not just the operations inside the constructor. This would mean that there is, indeed, a bug in Visual C++. However, it is still my view that the wording of the standard is not sufficiently clear, so I'll leave the rest of my answer unchanged for posterity.

To clarify: The behaviour mentioned by the OP is actually a bug, and in light of this, most of what I say below this update is wrong.

End Update

This is actually not a compiler bug, but rather a strange quirk of the standard, so your confusion is understandable.

According to the C++11 standard, the following is the condition for is_trivially_constructible::value to be 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

So, is_trivially_constructible is true as long as the given type is constructible with the given arguments and it does not call any non-trivial operations. Your example includes only one such constructor, a copy constructor. In fact, by the definition of "non-trivial operation" (essentially a non-trivial operator or constructor), this does hold for your type. So returning true is correct.

However, there is one very strange point! The C+11 standard says the following about copy constructors:

§12.8.12 (emphasis mine)

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.

Since you do provide a user-defined copy constructor your class is not trivially copy constructible. The copy constructor you've given is not trivial. Still, the non-trivial copy constructor does fulfil the necessary criteria for is_trivially_constructible to return true given an argument which matches your copy constructor.

In my mind, this seems to be more of a "bug" in the standard. is_trivially_constructible returns whether the type is trivially constructible given certain arguments. This does not seem to guarantee that the constructor itself is deemed trivial!

Update:

After trying to devise a test to show this following case, I did find a bug in VC11. The logic described by the standard means that, if B is used as a sub-object (member or base) of another type, any constructor of that type which invokes the copy constructor of B should be deemed non-trivial by std::is_trivially_constructible. This is not the case in VC11.

Example Code

#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;

}

这篇关于当 T 声明为 T::T(T&amp;) 时 std::is_trivially_constructible<T, T&&gt;::value 结果不正确的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆