在函数体外,decltype(*this) 相当于什么?

7

是否可能编写一个trait,用于返回其所使用的类的类型?如何在下面的示例中实现get_class?

class Foo {
    typedef get_class::type type; // type = Foo now
}; 

注意:我需要编写一个宏,该宏在类主体中进行扩展,用于多个类,因此我不能简单地编写“typedef Foo type;”

使用情况:

我有一个“reflectable(...)”宏,它生成基础设施以遍历成员、访问它们并使用它们的名称查找:

class Foo 
{
    friend std::ostream &operator<<(std::ostream &, const Foo&);
    reflectable(
         (int) bar,
         (double) baz
    )
}

reflectable(...)应该是一个宏,这样我就可以将类型和成员名称分别作为字符串获取,以构建地图进行查找。

我希望所有的可反射类都可以流式传输,但如果我将我的reflectable()宏放到私有部分,我必须向类添加友元声明。我希望将其移动到宏中:

friend std::ostream &operator<<(std::ostream &, const get_class::type&);

你可以将封闭类型名称作为宏参数接受。此外,一些编码标准要求每个类声明一个类类型别名成员。 - user7860670
1
如果您认为可行的话,您可以编写一个帮助器模板类,它将typedef其模板参数,并且您需要从Helper<Foo>派生出Foo - k.v.
@DeiDei 我已经更新了我的使用案例。 - simon
@k.v. 我不知道宏中的Foo,因此我无法实例化Helper类。 - simon
1
可能是如何在C++11中使用decltype引用当前类?的重复问题。 - cpplearner
显示剩余7条评论
4个回答

2
抱歉,但我相信在标准的C++中,无论如何你都必须传递类名给宏。 关于一些失败的尝试,请参见本答案末尾。
话虽如此,我认为你最好稍微改变一下你的设计。不要使用
friend std::ostream &operator<<(std::ostream &, const get_class::type&);

提供一个公共的成员函数

void stream_to (std::ostream &) const {
  // Implementation
}

与一个免费的函数模板一起。
template<typename T, typename std:: enable_if<has_stream_to<T>::value>::type * = nullptr>
std::ostream &operator<<(std::ostream & s, T const & t) {
  t.stream_to(s);
  return s;
}

位于您的libraries/programs命名空间中。(注意:我稍后将添加trait has_stream_to,对于急切搜索"C++"标签中的"检测成员函数"的人来说)


不起作用的方法:

  • 使用成员函数指针,使用模板函数提取类类型。原因:&foo在具有成员函数foo的类内部不会提供指向foo的成员函数指针。语法是必须的(由标准规定),为&C::foo(其中C是类名...)。

  • 使用数据成员指针,使用模板函数提取类类型。原因与上述相同。

  • 使用返回*this的专用成员函数,推断返回类型以获取类类型。原因:需要一个实例才能调用此函数。

  • 使用静态成员函数的指针。原因:这些指针与指向自由函数的指针相同,无法从中推导出类名。

  • 在成员初始化中使用this指针。原因:成员的类型需要以某种方式编码类类型,但auto不允许用于非静态数据成员。


@skypjack 你为什么删除了你的回答? - Daniel Jour
可以使用G++ 5.4编译,但是MSVC和clang会报错:无法在静态成员函数声明中使用'this'。 - simon
我已经使用类似的方法有一段时间了,在G++下运行得非常好。只有一个注意点,无论如何get_type()函数和type声明都应该是私有的,以防OP在使用多个类推断其类型的继承时出现问题。 - LoPiTaL
1
我删除了那段代码的答案,因为它是无效的。你应该从你的答案中将其删除。 - skypjack
2
我没有点踩,但请注意你的 stream_to 仍然必须是公共的才能使检测工作。此外,在非虚拟成员函数上使用 final 是不合法的。 - T.C.
显示剩余4条评论

0
注意:我必须编写一个宏,在类体中展开,用于多个类,所以我不能简单地写 'typedef Foo type;'
如果你可以在声明类时展开宏,而不是在类体中,像这样的方式可能起作用:
#include<type_traits>

template<typename T>
struct Type {
    using type = T;
};

#define struct_with_type(S) struct S: Type<S>

struct_with_type(Foo) {};

int main() {
    static_assert(std::is_same<Foo::type, Foo>::value, "!");
}

请注意,我之前回答中提供的示例不是有效的代码:

struct Foo {
    static constexpr auto get_type() -> std::decay_t<decltype(*this)>;
    using type = decltype(get_type());
};

由于编译器的一个错误,它只能使用GCC进行编译。
这是我不会在生产代码中使用的东西。

话虽如此,如果你喜欢它,以下代码可以编译并且与那个解决方案相似:

#include<type_traits>
#include<utility>

#define add_type() constexpr auto type() -> std::decay_t<decltype(*this)>;

template<typename T>
using get_type = decltype(std::declval<T>().type());

struct Foo {
    add_type();
};

int main() {
    static_assert(std::is_same<get_type<Foo>, Foo>::value, "!");
}

谢谢,不幸的是我不能使用你最后的代码片段,我需要在宏内部使用get_type,所以我不能用Foo来实例化它。另一方面,struct_with_type宏可能会起作用,但限制了额外的继承。 - simon
@simon 你可以轻松扩展这个宏来支持继承。 - skypjack

0

这不是一个非常好的解决方案,但对于简单函数可能会有用。我还没有将其扩展到模板和其他诸如可变参数函数之类的东西上:

#define FUNCTION_DECL(R, N, ...) \
        R N(__VA_ARGS__);

#define DEFINE_FRIEND_OPERATOR(CLASS_NAME, MEMBERS)                       \
    friend std::ostream& operator<<(std::ostream& s, CLASS_NAME const& f) \
    {                                                                     \
        std::cout << #CLASS_NAME << ':' << std::endl;                     \
        MEMBERS                                                           \
        return s;                                                         \
    }

#define S_(X) #X
#define S(X) S_(X)

#define STREAM_FUNCTION(R, N, ...) \
    s << "  " #R " " #N S((__VA_ARGS__)) << std::endl;

我没有看到任何便携式的方式来获取类名,除非将其传递给宏,因此类型特征被跳过。

现在,您可以将上述宏放置在某个适当的头文件中(并找到更好/更合适的名称...),该头文件将包含每个类。在那里,您将有:

class Foo
{
#define FOO_MEMBERS                    \
        MEMBER(int, f0, int, int, int) \
        MEMBER(double, f1, double)     \
        MEMBER(void, f2, char const)

public:
#ifdef MEMBER
#undef MEMBER
#endif
#define MEMBER FUNCTION_DECL
    FOO_MEMBERS
#undef MEMBER

private:
#define MEMBER STREAM_FUNCTION
    DEFINE_FRIEND_OPERATOR(Foo, FOO_MEMBERS)
#undef MEMBER
};

对于地图条目,您可以使用类似的宏,在构造函数中生成初始化器列表,如此处所示。


0

正如我在评论中提到的那样,这听起来像是一个 XY 问题。相反,添加一层间接性:

class Foo 
{
    friend class reflectable_access;
    reflectable(
         (int) bar,
         (double) baz
    )
};

现在,reflectable_access可以访问你的reflectable宏添加的内容,并将它们暴露给需要访问它们的其他人。

如果这是一个模板类,我可以将友元声明移到宏中,并在想要使用的地方实例化。 - simon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接