"Materializing" 用于 C++ 类型推断的已知类型的对象
"Materializing" an object of a known type for C++ type inference
下面的代码在我尝试过的每个 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
。实际代码是类型安全序列化逻辑的一部分。
问题是:是否有一种符合标准的方法 "materializing" 模板参数类型 T
的对象用于此代码中的 decltype
,或者是不可能的在没有源类型的预构造对象的情况下拥有这种符合标准的类型映射?
用指向对象的指针替换函数参数是丑陋的,并不能真正解决问题,因为在不引入另一个参数的情况下,一般情况下不清楚这些函数需要用 nullptr
参数做什么UB.
您可以使用 std::declval
:
template <typename T>
using base_type = decltype((get_base(std::declval<T>())));
您要找的是std::declval
。它是 returns 你给它的类型的函数,因此你可以在未评估的上下文中使用该类型的对象。那转
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>();
那么你的程序格式错误。
其实这段代码还可以。 (确实,解引用一个空指针并将结果绑定到一个引用是 UB,或者解引用一个空指针并访问结果左值。但是,当程序的执行实际上并不评估这些构造时,有没有 UB。)
但是 std::declval<T>()
确实是首选的成语。与空指针技巧不同,std::declval<T>()
是 "safe":如果您不小心在可能已求值的上下文中使用它,则会出现编译时错误。它也没有那么难看。
下面的代码在我尝试过的每个 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
。实际代码是类型安全序列化逻辑的一部分。
问题是:是否有一种符合标准的方法 "materializing" 模板参数类型 T
的对象用于此代码中的 decltype
,或者是不可能的在没有源类型的预构造对象的情况下拥有这种符合标准的类型映射?
用指向对象的指针替换函数参数是丑陋的,并不能真正解决问题,因为在不引入另一个参数的情况下,一般情况下不清楚这些函数需要用 nullptr
参数做什么UB.
您可以使用 std::declval
:
template <typename T>
using base_type = decltype((get_base(std::declval<T>())));
您要找的是std::declval
。它是 returns 你给它的类型的函数,因此你可以在未评估的上下文中使用该类型的对象。那转
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>();
那么你的程序格式错误。
其实这段代码还可以。 (确实,解引用一个空指针并将结果绑定到一个引用是 UB,或者解引用一个空指针并访问结果左值。但是,当程序的执行实际上并不评估这些构造时,有没有 UB。)
但是 std::declval<T>()
确实是首选的成语。与空指针技巧不同,std::declval<T>()
是 "safe":如果您不小心在可能已求值的上下文中使用它,则会出现编译时错误。它也没有那么难看。