gpt4 book ai didi

c++ - 模板代码中的类型不完整

转载 作者:IT老高 更新时间:2023-10-28 22:39:29 25 4
gpt4 key购买 nike

假设我们有两种类型(完整和不完整):

struct CompleteType{};

struct IncompleteType;

我们还有模板代码:
#include <type_traits>

template <typename = X(T)>
struct Test : std::false_type {};

template <>
struct Test<T> : std::true_type {};
T可以 CompleteTypeIncompleteType这里和 X(T)可以 T , decltype(T())decltype(T{}) (假设 X(T) 是一个宏)。

此代码按以下方式使用:
std::cout << std::boolalpha << Test<>::value << std::endl;

您可以在下面看到不同的编译器如何处理此类代码:

叮当 3.4
X(T) \ T       CompleteType  IncompleteType
T true true
decltype(T()) true --- (1, 2)
decltype(T{}) true --- (1, 2)
  • error: invalid use of incomplete type 'IncompleteType'即使在不使用 decltype(T()) 的情况下,即使在具有不完整类型的模板类声明上(对于 decltype(T{})T ,但不是简单的 Test<>::value )也给出在代码中。
  • error: too few template arguments for class template 'Test'


  • 克++ 4.8.1
    X(T) \ T       CompleteType  IncompleteType
    T true true
    decltype(T()) true true
    decltype(T{}) true true

    vc++ 18.00.21005.1
    X(T) \ T       CompleteType  IncompleteType
    T true true
    decltype(T()) true --- (1)
    decltype(T{}) true --- (2)
  • error C2514: 'IncompleteType' : class has no constructors
  • error C2440: '<function-style-cast>' : cannot convert from 'initializer-list' to 'IncompleteType' Source or target has incomplete type


  • 什么编译器按照标准运行? 请注意像 std::cout << typeid(X(IncompleteType)).name() << std::endl; 这样的简单字符串不会在所有编译器上编译 X 的所有变体(除了 vc++ X(T) == T )。

    最佳答案

    我相信在这种情况下,Clang 和 MSVC 的行为是符合标准的。我认为 GCC 在这里采取了一些捷径。

    让我们先把一些事实摆在桌面上。 decltype 的操作数表达式是所谓的未计算操作数,由于它们最终从未计算过,因此处理方式略有不同。

    特别是对类型完整的要求较少。基本上,如果您有任何临时对象(作为表达式中涉及的函数或运算符中的参数或返回值),它们不需要是完整的(参见第 5.2.2/11 和 7.1.6.2/5 节)。但这只是解除了通常的“不能声明不完整类型的对象”的限制,并没有解除对不完整类型的另一个限制,即“不能调用不完整类型的成员函数”。这就是踢球者。

    表达式 decltype(T())decltype(T{}) ,其中 T不完整,必须查找 T 类型的构造函数,因为它是该类的(特殊)成员函数。只是因为它是一个构造函数调用,这会造成一些歧义(即,它只是创建了一个临时对象?还是调用了一个构造函数?)。如果是任何其他成员功能,则不会有任何争论。幸运的是,该标准确实解决了这场争论:

    12.2/1

    Even when the creation of the temporary object is unevaluated (Clause 5) or otherwise avoided (12.8), all the semantic restrictions shall be respected as if the temporary object had been created and later destroyed. [ Note: even if there is no call to the destructor or copy/move constructor, all the semantic restrictions, such as accessibility (Clause 11) and whether the function is deleted (8.4.3), shall be satisfied. However, in the special case of a function call used as the operand of a decltype-specifier (5.2.2), no temporary is introduced, so the foregoing does not apply to the prvalue of any such function call. - end note ]



    最后一句可能有点困惑,但这仅适用于函数调用的返回值。换句话说,如果您有 T f();函数,然后您声明 decltype(f()) ,然后 T不需要完整或对是否有可用和可访问的构造函数/析构函数进行任何语义检查。

    事实上,这整个问题正是为什么存在 std::declval 的原因。实用程序,因为当您无法使用时 decltype(T()) ,您可以使用 decltype(std::declval<T>()) , 和 declval只不过是一个返回类型为 T 的纯右值的(假)函数.但当然, declval旨在用于不太重要的情况,例如 decltype( f( std::declval<T>() ) )哪里 f将是一个采用 T 类型对象的函数.和 declval不要求类型是完整的(见第 20.2.4 节)。这基本上是您解决整个问题的方法。

    因此,就 GCC 的行为而言,我认为它需要一条捷径,因为它试图找出 T() 的类型。或 T{}是。我认为一旦 GCC 发现 T引用一个类型名(不是函数名),它推断这是一个构造函数调用,因此,无论查找发现什么作为实际被调用的构造函数,返回类型都是 T (好吧,严格来说构造函数没有返回类型,但你明白我的意思)。这里的要点是,在未计算的表达式中,这可能是一个有用(更快)的捷径。但据我所知,这不是符合标准的行为。

    如果 GCC 允许 CompleteType如果构造函数被删除或私有(private),那么这也与标准的上述引用段落直接矛盾。在这种情况下,编译器需要强制执行所有语义限制,即使不计算表达式也是如此。

    Note that simple string like std::cout << typeid(X(IncompleteType)).name() << std::endl; does not compile on all compilers for all variants of X (except for vc++ and X(T) == T).



    这是预期的(除了 MSVC 和 X(T) == T)。 typeidsizeof运算符类似于 decltype然而,从它们的操作数未计算的意义上讲,它们都有一个额外的要求,即结果表达式的类型必须是完整类型。可以想象,编译器可以解析 typeid对于不完整的类型(或至少,具有部分类型信息),但标准需要一个完整的类型,这样编译器就不必这样做了。我想这就是 MSVC 正在做的事情。

    因此,在这种情况下, T()T{}案例失败的原因与 decltype 相同(正如我刚刚解释的),以及 X(T) == T案例失败,因为 typeid需要一个完整的类型(但 MSVC 设法解除了该要求)。在 GCC 上,它由于 typeid 而失败需要所有 X(T) 的完整类型情况(即 GCC 采取的捷径不会影响 sizeoftypeid 的结果)。

    所以,总而言之,我认为 Clang 是三者中最符合标准的(不走捷径或进行扩展)。

    关于c++ - 模板代码中的类型不完整,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22770318/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com