2406C++,ADL加隐式转换

发布于:2024-06-02 ⋅ 阅读:(75) ⋅ 点赞:(0)

原文
最近在搞iguana.struct_pb动态反射功能时,遇见一个奇怪的问题.

struct person {
  std::string name;
  int64_t age;
};
REFLECTION(person, name, age);
struct persons {
  std::vector<person> list;
};
REFLECTION(persons, list);
//#1
static_assert(iguana::is_public_reflection_v<persons>);

#1代码是符合期望的,因为person人们都是iguana可反射对象,is_reflection_v的实现也比较简单:

template <typename T, typename = void>
struct is_public_reflection : std::false_type {};
template <typename T>
struct is_public_reflection<
    T, std::void_t<decltype(iguana_reflect_members(std::declval<T>()))>>
    : std::true_type {};
template <typename T>
constexpr bool is_public_reflection_v = is_public_reflection<T>::value;

void_t来检查T对象是否匹配iguana_reflect_members函数的调用,函数签名如下:

template<typename T>
inline static auto iguana_reflect_members(const T& t);

只要结构定义了REFLECTION宏,就一定匹配iguana_reflect_members调用函数,目前一切正常,但是给结构加一行代码后就有问题了.

struct persons {
  persons(std::vector<person> s) : list(std::move(s)) {}
  std::vector<person> list;//..
};
REFLECTION(persons, list);

人们加了一个构造器,然后这样静态检查:

static_assert(iguana::is_public_reflection_v<std::vector<person>>);

惊讶的是通过了该检查,是的!为什么?因为隐式转换.

原来可按人们隐式转换std::vector,而人们则是个可反射对象,所以错误的通过了.
变通避免隐式转换,在构造器前加explicit,但该方法治标不治本.

另外或将ADL函数的参数改成指针,std::vector无法转换成人们指针的.但是仍不够,如果有个从人们继承的类,继承类指针隐式转换基类指针的,仍有漏洞.

最好是用个identity包装一下ADL参数,这样,外面怎么隐式转换,也无法转换identity了.

template<typename T>
struct identity{};
template<typename T>
inline static auto iguana_reflect_members(const identity<T>& t);

至此解决问题,隐式转换的坑还真不小,需要特别注意.

另外预告一下iguana.struct_pb动态反射功能:
1,根据类型名创建对象实例
2,取对象所有的字段名
3,根据字段名和实例取置字段的值.