C++ 缺乏的等效PHP的self关键字,其评估的封闭类的类型.
在每个类的基础上伪造它很容易:
struct Foo
{
typedef Foo self;
};
Run Code Online (Sandbox Code Playgroud)
但我不得不再写Foo一遍.也许有一天我会弄错,导致一个无声的错误.
我可以使用一些decltype朋友和朋友的组合来"自主地"完成这项工作吗?我已尝试过以下内容但this在该地方无效:
struct Foo
{
typedef decltype(*this) self;
};
// main.cpp:3:22: error: invalid use of 'this' at top level
// typedef decltype(*this) self;
Run Code Online (Sandbox Code Playgroud)
(我不会担心相同的static,相同但后期绑定.)
Seb*_*ann 38
一种可能的解决方法(因为您仍需要编写一次类型):
template<typename T>
struct Self
{
protected:
typedef T self;
};
struct Foo : public Self<Foo>
{
void test()
{
self obj;
}
};
Run Code Online (Sandbox Code Playgroud)
为了更安全的版本,我们可以确保T实际上来自Self<T>:
Self()
{
static_assert(std::is_base_of<Self<T>, T>::value, "Wrong type passed to Self");
}
Run Code Online (Sandbox Code Playgroud)
请注意,static_assert成员函数内部可能是检查的唯一方法,因为传递的类型std::is_base_of必须完整.
Ral*_*zky 38
以下是如何在不重复Foo类型的情况下执行此操作:
template <typename...Ts>
class Self;
template <typename X, typename...Ts>
class Self<X,Ts...> : public Ts...
{
protected:
typedef X self;
};
#define WITH_SELF(X) X : public Self<X>
#define WITH_SELF_DERIVED(X,...) X : public Self<X,__VA_ARGS__>
class WITH_SELF(Foo)
{
void test()
{
self foo;
}
};
Run Code Online (Sandbox Code Playgroud)
如果要从中派生,Foo则应按WITH_SELF_DERIVED以下方式使用宏:
class WITH_SELF_DERIVED(Bar,Foo)
{
/* ... */
};
Run Code Online (Sandbox Code Playgroud)
您甚至可以根据需要使用尽可能多的基类进行多重继承(感谢可变参数模板和可变参数宏):
class WITH_SELF(Foo2)
{
/* ... */
};
class WITH_SELF_DERIVED(Bar2,Foo,Foo2)
{
/* ... */
};
Run Code Online (Sandbox Code Playgroud)
我已经验证了这可以用于gcc 4.8和clang 3.4.
Bar*_*icz 33
您可以使用宏而不是常规类声明,这将为您执行此操作.
#define CLASS_WITH_SELF(X) class X { typedef X self;
Run Code Online (Sandbox Code Playgroud)
然后使用喜欢
CLASS_WITH_SELF(Foo)
};
Run Code Online (Sandbox Code Playgroud)
#define END_CLASS }; 可能有助于提高可读性.
您也可以使用@ Paranaix Self并使用它(它开始变得非常hackish)
#define WITH_SELF(X) X : public Self<X>
class WITH_SELF(Foo) {
};
Run Code Online (Sandbox Code Playgroud)
Kon*_*lph 31
我没有正面证据,但我认为这是不可能的.以下失败 - 出于与您的尝试相同的原因 - 我认为这是我们能得到的最远的:
struct Foo {
auto self_() -> decltype(*this) { return *this; }
using self = decltype(self_());
};
Run Code Online (Sandbox Code Playgroud)
从本质上讲,这表明我们想要声明我们的typedef的范围根本没有访问权限(无论是直接的还是间接的)this,并且没有其他(编译器独立的)方式来获取类的类型或名称.
小智 21
在GCC和clang中都有用的是创建一个typedef,它this通过this在函数typedef的trailing-return-type中使用来引用.由于这不是静态成员函数的声明,因此this可以容忍使用.然后,您可以使用该typedef进行定义self.
#define DEFINE_SELF() \
typedef auto _self_fn() -> decltype(*this); \
using self = decltype(((_self_fn*)0)())
struct Foo {
DEFINE_SELF();
};
struct Bar {
DEFINE_SELF();
};
Run Code Online (Sandbox Code Playgroud)
不幸的是,严格阅读标准说即使这是无效的.clang做的是检查this静态成员函数的定义中没有使用的.在这里,它确实不是.GCC并不介意是否this在尾随返回类型中使用它而不管函数的类型如何,它甚至允许它用于static成员函数.但是,标准实际要求的this是不在非静态成员函数(或非静态数据成员初始化程序)的定义之外使用.英特尔做对了并拒绝这一点.
鉴于:
this 仅允许在非静态数据成员初始化器和非静态成员函数([expr.prim.general] p5)中,this评估的上下文中,也可以使用([over.call.func] p3),我想我可以确定地说,如果self不以某种方式包含类型名称,就没有办法实现.
编辑:我之前的推理有一个缺陷."非静态成员函数只能通过非限定名称调用,即使在未评估的上下文中,当可以使用它时([over.call.func] p3),"是不正确的.它实际上是说什么
如果关键字
this(9.3.2)在范围内并且引用了类T或派生类T,那么隐含的对象参数是(*this).如果关键字this不在范围内或引用另一个类,则类型的设计对象将T成为隐含的对象参数.如果参数列表由设计对象增强并且重载决策选择其中一个非静态成员函数T,则调用是格式错误的.
在静态成员函数内部,this可能不会出现,但它仍然存在.
但是,根据注释,在静态成员函数内部,将不执行f()to 的转换(*this).f(),并且未执行转换,则违反[expr.call] p1:
[...]对于成员函数调用,后缀表达式应为隐式(9.3.1,9.4)或显式类成员访问(5.2.5),其[...]
因为没有会员访问权限.所以即使这样也行不通.
Yak*_*ont 17
#define SELF_CHECK( SELF ) void self_check() { static_assert( std::is_same< typename std::decay<decltype(*this)>::type, SELF >::value, "self wrong type" ); }
#define SELF(T) typedef T self; SELF_CHECK(T)
struct Foo {
SELF(Foo); // works, self is defined as `Foo`
};
struct Bar {
SELF(Foo); // fails
};
Run Code Online (Sandbox Code Playgroud)
这不适用于模板类型,因为self_check未调用,因此static_assert不进行评估.
我们可以做一些黑客攻击以使其适用于templates,但它的运行时间成本较低.
#define TESTER_HELPER_TYPE \
template<typename T, std::size_t line> \
struct line_tester_t { \
line_tester_t() { \
static_assert( std::is_same< decltype(T::line_tester), line_tester_t<T,line> >::value, "test failed" ); \
static_assert( std::is_same< decltype(&T::static_test_zzz), T*(*)() >::value, "test 2 failed" ); \
} \
}
#define SELF_CHECK( SELF ) void self_check() { static_assert( std::is_same< typename std::decay<decltype(*this)>::type, SELF >::value, "self wrong type" ); }
#define SELF(T) typedef T self; SELF_CHECK(T); static T* static_test_zzz() { return nullptr; }; TESTER_HELPER_TYPE; line_tester_t<T,__LINE__> line_tester
Run Code Online (Sandbox Code Playgroud)
struct在您的类中创建一个大小为1字节的空.如果您的类型被实例化,self则进行测试.
Dan*_*rey 11
我也认为这是不可能的,这是另一个失败但恕我直言的有趣尝试,它避免了 - this访问:
template<typename T>
struct class_t;
template<typename T, typename R>
struct class_t< R (T::*)() > { using type = T; };
struct Foo
{
void self_f(); using self = typename class_t<decltype(&self_f)>::type;
};
#include <type_traits>
int main()
{
static_assert( std::is_same< Foo::self, Foo >::value, "" );
}
Run Code Online (Sandbox Code Playgroud)
失败的原因self_f是,当你想要获取它的地址时,C++要求你符合该类的资格:(
小智 8
我最近发现*this允许使用支撑或等于初始化器.在第5.1.1节(来自n3337工作草案)中描述:
3 [..]与其他上下文中的对象表达式不同
*this,为了成员函数体之外的类成员访问(5.2.5),不要求它是完整类型.[..]4否则,如果member-declarator声明类X的非静态数据成员(9.2),则表达式
this是可选的brace-or-equal-initializer中类型为"指向X的指针"的prvalue .它不应出现在成员声明者的其他地方.5该表达
this不得出现在任何其他背景下.[ 例如:Run Code Online (Sandbox Code Playgroud)class Outer { int a[sizeof(*this)]; // error: not inside a member function unsigned int sz = sizeof(*this); // OK: in brace-or-equal-initializer void f() { int b[sizeof(*this)]; // OK struct Inner { int c[sizeof(*this)]; // error: not inside a member function of Inner }; } };- 结束例子 ]
考虑到这一点,以下代码:
struct Foo
{
Foo* test = this;
using self = decltype(test);
static void smf()
{
self foo;
}
};
#include <iostream>
#include <type_traits>
int main()
{
static_assert( std::is_same< Foo::self, Foo* >::value, "" );
}
Run Code Online (Sandbox Code Playgroud)
通过丹尼尔弗雷的 static_assert.
终于找到合适的解决办法了!这个想法是由github 上的 @MitalAshok提出的。
#include <type_traits>
namespace SelfType
{
template <typename T>
struct Reader
{
friend auto adl_GetSelfType(Reader<T>);
};
template <typename T, typename U>
struct Writer
{
friend auto adl_GetSelfType(Reader<T>){return U{};}
};
inline void adl_GetSelfType() {}
template <typename T>
using Read = std::remove_pointer_t<decltype(adl_GetSelfType(Reader<T>{}))>;
}
#define DEFINE_SELF \
struct _self_type_tag {}; \
constexpr auto _self_type_helper() -> decltype(::SelfType::Writer<_self_type_tag, decltype(this)>{}, void()) {} \
using Self = ::SelfType::Read<_self_type_tag>;
Run Code Online (Sandbox Code Playgroud)
然后:
struct A
{
DEFINE_SELF
static_assert(std::is_same_v<Self, A>);
};
Run Code Online (Sandbox Code Playgroud)
这使用有状态模板元编程将类型存储在可访问的上下文中(在辅助函数的尾随返回类型中),然后读取否则无法访问的类型(在类范围内)。
这里的关键是 writer 是如何实例化的。简单地这样做auto _self_type_helper() -> Writer<...> {return {};}是行不通的:编写器被实例化时有延迟,使得状态可以在任何成员函数体内访问,但不能在类范围内访问。
但是,如果您这样做-> decltype(Writer<...>{}, void()),或者以其他方式使编写器成为影响类型的表达式的一部分,那么它就会开始工作。我不完全确定为什么会发生这种情况。
| 归档时间: |
|
| 查看次数: |
10364 次 |
| 最近记录: |