decltype(* this)等效的外部函数体

时间:2017-06-19 10:34:16

标签: c++ c++11 typetraits

是否可以编写一个特征,这会导致它所使用的类的类型?如何在下面的例子中实现get_class?

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

注意:我必须编写一个宏,它在类体中扩展,用于多个类,所以我不能简单地写'typedef Foo type;'

用例:

我有一个可反射的(...)宏,它生成基础设施来迭代成员,访问它们并使用它们的名称查找它们:

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&);

4 个答案:

答案 0 :(得分: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;
}

在您的库/程序命名空间中。 (注意:我稍后会添加特征has_stream_to,以便不耐烦地搜索&#34;检测成员函数&#34;在C ++标记内)

工作的方法:

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

  • 使用指向数据成员的指针,使用模板函数提取类类型。与上述理由相同。

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

  • 使用指向静态成员函数的指针。原因:这些指针与指向自由函数的指针相同,类名不能从它们推导出来。

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

答案 1 :(得分:0)

  

注意:我必须编写一个宏,它在类体中扩展,用于多个类,因此我不能简单地写出&#39; typedef Foo类型;&#39;

如果你可以在声明类而不是在类体中扩展宏时,这样的东西可以工作:

#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, "!");
}

答案 2 :(得分: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
};

对于地图条目,您有类似的宏,在构造函数中生成初始化列表,如显示here

答案 3 :(得分:0)

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

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

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