【问题标题】:"Materializing" an object of a known type for C++ type inference“物化”已知类型的对象以进行 C++ 类型推断
【发布时间】:2020-02-22 04:05:01
【问题描述】:

在我尝试过的每个 C++11 及更高版本的编译器中,以下代码都可以满足我的所有需求。因此,在实践中,就我的目的而言,它是有效的(并且由于历史原因,在可预见的将来,它至少可以在 Linux 上工作)。然而,从语言律师的角度来看,这段代码是无效的,因为它包含一个形式上是 UD 的构造(指向不存在对象的指针的解引用),即使这种解引用实际上从未执行过。

#include <type_traits>
#include <iostream>

namespace n1 {

struct int_based { int base = 0; };
inline int get_base(int_based ib) { return ib.base; }

}

namespace n2 {

struct double_based;
double get_base(const double_based&);

}

template <typename T>
using base_type = decltype((get_base(*(T*)nullptr)));

int main() {
    auto isInt = std::is_same<base_type<n1::int_based>, int>::value;
    auto isDouble = std::is_same<base_type<n2::double_based>, double>::value;
    auto unlike = std::is_same<base_type<n1::int_based>, double>::value;
    std::cout << isInt << isDouble << unlike << std::endl;
    return 0;
}

代码执行 Koenig 查找类型映射并使用我不想更改的函数签名推断映射类型。在本例中,double_based 类型不完整;在我的实际用例中,类型应该是完整的,但不保证是DefaultConstructible。实际代码是类型安全序列化逻辑的一部分。

问题是:是否有一种符合标准的方式来“物化”模板参数类型 T 的对象以用于此代码中的 decltype,或者是否不可能有这种符合标准的类型映射没有源类型的预构造对象?

用指向对象的指针替换函数参数是丑陋的,并不能真正解决问题,因为在不引入另一个 UB 的情况下,在一般情况下不清楚这些函数需要用 nullptr 参数做什么。

【问题讨论】:

    标签: c++ c++11 undefined-behavior type-inference


    【解决方案1】:

    您正在寻找的是std::declval。它是一个返回您给它的类型的函数,因此您可以在未评估的上下文中使用该类型的对象。转了

    template <typename T>
    using base_type = decltype((get_base(*(T*)nullptr)));
    

    进入

    template <typename T>
    using base_type = decltype((get_base(std::declval<T>())));
    

    请注意,不需要定义 std::declval。如果你尝试使用

    T foo = std::declval<T>();
    

    那么你的程序格式不正确。

    【讨论】:

    • 嗯,对于我的用例,它实际上是template &lt;typename T&gt; using base_type = decltype((get_base(std::declval&lt;T&amp;&gt;())));。否则结果不完全等价。
    • @Kit。是啊。 std::declval&lt;T&gt;() 给你一个右值。 std::declval&lt;T&amp;&gt;() 给你一个左值。这可以/将会有所作为。
    【解决方案2】:

    其实这段代码没问题。 (确实是UB取消引用空指针并将结果绑定到引用,或者取消引用空指针并访问结果左值。但是,当程序的执行实际上并没有评估这些构造时,有没有UB。)

    std::declval&lt;T&gt;() 确实是首选的成语。与空指针技巧不同,std::declval&lt;T&gt;() 是“安全的”:如果您不小心在可能评估的上下文中使用它,则会出现编译时错误。它也没有那么难看。

    【讨论】:

      【解决方案3】:

      你可以使用std::declval:

      template <typename T>
      using base_type = decltype((get_base(std::declval<T>())));
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-18
        • 1970-01-01
        • 2019-06-08
        • 2011-05-29
        • 1970-01-01
        • 2013-08-27
        相关资源
        最近更新 更多